screen_13/graph/
resolver.rs

1use {
2    super::{
3        Area, Attachment, Binding, Bindings, ExecutionPipeline, Node, NodeIndex, Pass, RenderGraph,
4        node::SwapchainImageNode,
5        pass_ref::{Subresource, SubresourceAccess},
6    },
7    crate::{
8        driver::{
9            AttachmentInfo, AttachmentRef, CommandBuffer, CommandBufferInfo, Descriptor,
10            DescriptorInfo, DescriptorPool, DescriptorPoolInfo, DescriptorSet, DriverError,
11            FramebufferAttachmentImageInfo, FramebufferInfo, RenderPass, RenderPassInfo,
12            SubpassDependency, SubpassInfo,
13            accel_struct::AccelerationStructure,
14            buffer::Buffer,
15            format_aspect_mask,
16            graphic::{DepthStencilMode, GraphicPipeline},
17            image::{Image, ImageAccess, ImageViewInfo},
18            image_access_layout, initial_image_layout_access, is_read_access, is_write_access,
19            pipeline_stage_access_flags,
20            swapchain::SwapchainImage,
21            vk_sync::{
22                AccessType, BufferBarrier, GlobalBarrier, ImageBarrier, cmd::pipeline_barrier,
23            },
24        },
25        pool::{Lease, Pool},
26    },
27    ash::vk,
28    log::{
29        Level::{Debug, Trace},
30        debug, log_enabled, trace,
31    },
32    std::{
33        cell::RefCell,
34        collections::{BTreeMap, HashMap, VecDeque},
35        iter::repeat,
36        ops::Range,
37    },
38};
39
40#[cfg(not(debug_assertions))]
41use std::hint::unreachable_unchecked;
42
43#[derive(Default)]
44struct AccessCache {
45    accesses: Vec<bool>,
46    binding_count: usize,
47    read_count: Vec<usize>,
48    reads: Vec<usize>,
49}
50
51impl AccessCache {
52    /// Finds the unique indexes of the node bindings which a given pass reads. Results are
53    /// returned in the opposite order the dependencies must be resolved in.
54    ///
55    /// Dependent upon means that the node is read from the pass.
56    #[profiling::function]
57    fn dependent_nodes(&self, pass_idx: usize) -> impl ExactSizeIterator<Item = usize> + '_ {
58        let pass_start = pass_idx * self.binding_count;
59        let pass_end = pass_start + self.read_count[pass_idx];
60        self.reads[pass_start..pass_end].iter().copied()
61    }
62
63    /// Finds the unique indexes of the passes which write to a given node; with the restriction
64    /// to not inspect later passes. Results are returned in the opposite order the dependencies
65    /// must be resolved in.
66    ///
67    /// Dependent upon means that the pass writes to the node.
68    #[profiling::function]
69    fn dependent_passes(
70        &self,
71        node_idx: usize,
72        end_pass_idx: usize,
73    ) -> impl Iterator<Item = usize> + '_ {
74        self.accesses[node_idx..end_pass_idx * self.binding_count]
75            .iter()
76            .step_by(self.binding_count)
77            .enumerate()
78            .rev()
79            .filter_map(|(pass_idx, write)| write.then_some(pass_idx))
80    }
81
82    /// Returns the unique indexes of the passes which are dependent on the given pass.
83    #[profiling::function]
84    fn interdependent_passes(
85        &self,
86        pass_idx: usize,
87        end_pass_idx: usize,
88    ) -> impl Iterator<Item = usize> + '_ {
89        self.dependent_nodes(pass_idx)
90            .flat_map(move |node_idx| self.dependent_passes(node_idx, end_pass_idx))
91    }
92
93    fn update(&mut self, graph: &RenderGraph, end_pass_idx: usize) {
94        self.binding_count = graph.bindings.len();
95
96        let cache_len = self.binding_count * end_pass_idx;
97
98        self.accesses.truncate(cache_len);
99        self.accesses.fill(false);
100        self.accesses.resize(cache_len, false);
101
102        self.read_count.clear();
103
104        self.reads.truncate(cache_len);
105        self.reads.fill(usize::MAX);
106        self.reads.resize(cache_len, usize::MAX);
107
108        thread_local! {
109            static NODES: RefCell<Vec<bool>> = Default::default();
110        }
111
112        NODES.with_borrow_mut(|nodes| {
113            nodes.truncate(self.binding_count);
114            nodes.fill(true);
115            nodes.resize(self.binding_count, true);
116
117            for (pass_idx, pass) in graph.passes[0..end_pass_idx].iter().enumerate() {
118                let pass_start = pass_idx * self.binding_count;
119                let mut read_count = 0;
120
121                for (&node_idx, accesses) in pass.execs.iter().flat_map(|exec| exec.accesses.iter())
122                {
123                    self.accesses[pass_start + node_idx] = true;
124
125                    if nodes[node_idx] && is_read_access(accesses.first().unwrap().access) {
126                        self.reads[pass_start + read_count] = node_idx;
127                        nodes[node_idx] = false;
128                        read_count += 1;
129                    }
130                }
131
132                if pass_idx + 1 < end_pass_idx {
133                    nodes.fill(true);
134                }
135
136                self.read_count.push(read_count);
137            }
138        });
139    }
140}
141
142struct ImageSubresourceRangeDebug(vk::ImageSubresourceRange);
143
144impl std::fmt::Debug for ImageSubresourceRangeDebug {
145    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146        self.0.aspect_mask.fmt(f)?;
147
148        f.write_str(" array: ")?;
149
150        let array_layers = self.0.base_array_layer..self.0.base_array_layer + self.0.layer_count;
151        array_layers.fmt(f)?;
152
153        f.write_str(" mip: ")?;
154
155        let mip_levels = self.0.base_mip_level..self.0.base_mip_level + self.0.level_count;
156        mip_levels.fmt(f)
157    }
158}
159
160#[derive(Debug)]
161struct PhysicalPass {
162    descriptor_pool: Option<Lease<DescriptorPool>>,
163    exec_descriptor_sets: HashMap<usize, Vec<DescriptorSet>>,
164    render_pass: Option<Lease<RenderPass>>,
165}
166
167impl Drop for PhysicalPass {
168    fn drop(&mut self) {
169        self.exec_descriptor_sets.clear();
170        self.descriptor_pool = None;
171    }
172}
173
174/// A structure which can read and execute render graphs. This pattern was derived from:
175///
176/// <http://themaister.net/blog/2017/08/15/render-graphs-and-vulkan-a-deep-dive/>
177/// <https://github.com/EmbarkStudios/kajiya>
178#[derive(Debug)]
179pub struct Resolver {
180    pub(super) graph: RenderGraph,
181    physical_passes: Vec<PhysicalPass>,
182}
183
184impl Resolver {
185    pub(super) fn new(graph: RenderGraph) -> Self {
186        let physical_passes = Vec::with_capacity(graph.passes.len());
187
188        Self {
189            graph,
190            physical_passes,
191        }
192    }
193
194    #[profiling::function]
195    fn allow_merge_passes(lhs: &Pass, rhs: &Pass) -> bool {
196        fn first_graphic_pipeline(pass: &Pass) -> Option<&GraphicPipeline> {
197            pass.execs
198                .first()
199                .and_then(|exec| exec.pipeline.as_ref().map(ExecutionPipeline::as_graphic))
200                .flatten()
201        }
202
203        fn is_multiview(view_mask: u32) -> bool {
204            view_mask != 0
205        }
206
207        let lhs_pipeline = first_graphic_pipeline(lhs);
208        if lhs_pipeline.is_none() {
209            trace!("  {} is not graphic", lhs.name,);
210
211            return false;
212        }
213
214        let rhs_pipeline = first_graphic_pipeline(rhs);
215        if rhs_pipeline.is_none() {
216            trace!("  {} is not graphic", rhs.name,);
217
218            return false;
219        }
220
221        let lhs_pipeline = unsafe { lhs_pipeline.unwrap_unchecked() };
222        let rhs_pipeline = unsafe { rhs_pipeline.unwrap_unchecked() };
223
224        // Must be same general rasterization modes
225        if lhs_pipeline.info.blend != rhs_pipeline.info.blend
226            || lhs_pipeline.info.cull_mode != rhs_pipeline.info.cull_mode
227            || lhs_pipeline.info.front_face != rhs_pipeline.info.front_face
228            || lhs_pipeline.info.polygon_mode != rhs_pipeline.info.polygon_mode
229            || lhs_pipeline.info.samples != rhs_pipeline.info.samples
230        {
231            trace!("  different rasterization modes",);
232
233            return false;
234        }
235
236        let rhs = rhs.execs.first();
237
238        // PassRef makes sure this never happens
239        debug_assert!(rhs.is_some());
240
241        let rhs = unsafe { rhs.unwrap_unchecked() };
242
243        let mut common_color_attachment = false;
244        let mut common_depth_attachment = false;
245
246        // Now we need to know what the subpasses (we may have prior merges) wrote
247        for lhs in lhs.execs.iter().rev() {
248            // Multiview subpasses cannot be combined with non-multiview subpasses
249            if is_multiview(lhs.view_mask) != is_multiview(rhs.view_mask) {
250                trace!("  incompatible multiview");
251
252                return false;
253            }
254
255            // Compare individual color attachments for compatibility
256            for (attachment_idx, lhs_attachment) in lhs
257                .color_attachments
258                .iter()
259                .chain(lhs.color_loads.iter())
260                .chain(lhs.color_stores.iter())
261                .chain(
262                    lhs.color_clears
263                        .iter()
264                        .map(|(attachment_idx, (attachment, _))| (attachment_idx, attachment)),
265                )
266                .chain(
267                    lhs.color_resolves
268                        .iter()
269                        .map(|(attachment_idx, (attachment, _))| (attachment_idx, attachment)),
270                )
271            {
272                let rhs_attachment = rhs
273                    .color_attachments
274                    .get(attachment_idx)
275                    .or_else(|| rhs.color_loads.get(attachment_idx))
276                    .or_else(|| rhs.color_stores.get(attachment_idx))
277                    .or_else(|| {
278                        rhs.color_clears
279                            .get(attachment_idx)
280                            .map(|(attachment, _)| attachment)
281                    })
282                    .or_else(|| {
283                        rhs.color_resolves
284                            .get(attachment_idx)
285                            .map(|(attachment, _)| attachment)
286                    });
287
288                if !Attachment::are_compatible(Some(*lhs_attachment), rhs_attachment.copied()) {
289                    trace!("  incompatible color attachments");
290
291                    return false;
292                }
293
294                common_color_attachment = true;
295            }
296
297            // Compare depth/stencil attachments for compatibility
298            let lhs_depth_stencil = lhs
299                .depth_stencil_attachment
300                .or(lhs.depth_stencil_load)
301                .or(lhs.depth_stencil_store)
302                .or_else(|| lhs.depth_stencil_resolve.map(|(attachment, ..)| attachment))
303                .or_else(|| lhs.depth_stencil_clear.map(|(attachment, _)| attachment));
304
305            let rhs_depth_stencil = rhs
306                .depth_stencil_attachment
307                .or(rhs.depth_stencil_load)
308                .or(rhs.depth_stencil_store)
309                .or_else(|| rhs.depth_stencil_resolve.map(|(attachment, ..)| attachment))
310                .or_else(|| rhs.depth_stencil_clear.map(|(attachment, _)| attachment));
311
312            if !Attachment::are_compatible(lhs_depth_stencil, rhs_depth_stencil) {
313                trace!("  incompatible depth/stencil attachments");
314
315                return false;
316            }
317
318            common_depth_attachment |= lhs_depth_stencil.is_some() && rhs_depth_stencil.is_some();
319        }
320
321        // Keep color and depth on tile.
322        if common_color_attachment || common_depth_attachment {
323            trace!("  merging due to common image");
324
325            return true;
326        }
327
328        // Keep input on tile
329        if !rhs_pipeline.input_attachments.is_empty() {
330            trace!("  merging due to subpass input");
331
332            return true;
333        }
334
335        trace!("  not merging");
336
337        // No reason to merge, so don't.
338        false
339    }
340
341    // See https://vulkan.lunarg.com/doc/view/1.3.204.1/linux/1.3-extensions/vkspec.html#attachment-type-imagelayout
342    fn attachment_layout(
343        aspect_mask: vk::ImageAspectFlags,
344        is_random_access: bool,
345        is_input: bool,
346    ) -> vk::ImageLayout {
347        if aspect_mask.contains(vk::ImageAspectFlags::COLOR) {
348            if is_input {
349                vk::ImageLayout::GENERAL
350            } else {
351                vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL
352            }
353        } else if aspect_mask.contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL)
354        {
355            if is_random_access {
356                if is_input {
357                    vk::ImageLayout::GENERAL
358                } else {
359                    vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL
360                }
361            } else {
362                vk::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL
363            }
364        } else if aspect_mask.contains(vk::ImageAspectFlags::DEPTH) {
365            if is_random_access {
366                if is_input {
367                    vk::ImageLayout::GENERAL
368                } else {
369                    vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL
370                }
371            } else {
372                vk::ImageLayout::DEPTH_READ_ONLY_OPTIMAL
373            }
374        } else if aspect_mask.contains(vk::ImageAspectFlags::STENCIL) {
375            if is_random_access {
376                if is_input {
377                    vk::ImageLayout::GENERAL
378                } else {
379                    vk::ImageLayout::STENCIL_ATTACHMENT_OPTIMAL
380                }
381            } else {
382                vk::ImageLayout::STENCIL_READ_ONLY_OPTIMAL
383            }
384        } else {
385            vk::ImageLayout::UNDEFINED
386        }
387    }
388
389    #[profiling::function]
390    fn begin_render_pass(
391        cmd_buf: &CommandBuffer,
392        bindings: &[Binding],
393        pass: &Pass,
394        physical_pass: &mut PhysicalPass,
395        render_area: Area,
396    ) -> Result<(), DriverError> {
397        trace!("  begin render pass");
398
399        let render_pass = physical_pass.render_pass.as_mut().unwrap();
400        let attachment_count = render_pass.info.attachments.len();
401
402        let mut attachments = Vec::with_capacity(attachment_count);
403        attachments.resize(
404            attachment_count,
405            FramebufferAttachmentImageInfo {
406                flags: vk::ImageCreateFlags::empty(),
407                usage: vk::ImageUsageFlags::empty(),
408                width: 0,
409                height: 0,
410                layer_count: 0,
411                view_formats: vec![],
412            },
413        );
414
415        thread_local! {
416            static CLEARS_VIEWS: RefCell<(Vec<vk::ClearValue>, Vec<vk::ImageView>)> = Default::default();
417        }
418
419        CLEARS_VIEWS.with_borrow_mut(|(clear_values, image_views)| {
420            clear_values.resize_with(attachment_count, vk::ClearValue::default);
421            image_views.resize(attachment_count, vk::ImageView::null());
422
423            for exec in &pass.execs {
424                for (attachment_idx, (attachment, clear_value)) in &exec.color_clears {
425                    let attachment_image = &mut attachments[*attachment_idx as usize];
426                    if let Err(idx) = attachment_image
427                        .view_formats
428                        .binary_search(&attachment.format)
429                    {
430                        clear_values[*attachment_idx as usize] = vk::ClearValue {
431                            color: vk::ClearColorValue {
432                                float32: clear_value.0,
433                            },
434                        };
435
436                        let image = bindings[attachment.target].as_driver_image().unwrap();
437
438                        attachment_image.flags = image.info.flags;
439                        attachment_image.usage = image.info.usage;
440                        attachment_image.width = image.info.width >> attachment.base_mip_level;
441                        attachment_image.height = image.info.height >> attachment.base_mip_level;
442                        attachment_image.layer_count = attachment.array_layer_count;
443                        attachment_image.view_formats.insert(idx, attachment.format);
444
445                        image_views[*attachment_idx as usize] = Image::view(
446                            image,
447                            ImageViewInfo {
448                                array_layer_count: attachment.array_layer_count,
449                                aspect_mask: attachment.aspect_mask,
450                                base_array_layer: attachment.base_array_layer,
451                                base_mip_level: attachment.base_mip_level,
452                                fmt: attachment.format,
453                                mip_level_count: attachment.mip_level_count,
454                                ty: image.info.ty,
455                            },
456                        )?;
457                    }
458                }
459
460                for (attachment_idx, attachment) in exec
461                    .color_attachments
462                    .iter()
463                    .chain(&exec.color_loads)
464                    .chain(&exec.color_stores)
465                    .chain(exec.color_resolves.iter().map(
466                        |(dst_attachment_idx, (attachment, _))| (dst_attachment_idx, attachment),
467                    ))
468                {
469                    let attachment_image = &mut attachments[*attachment_idx as usize];
470                    if let Err(idx) = attachment_image
471                        .view_formats
472                        .binary_search(&attachment.format)
473                    {
474                        let image = bindings[attachment.target].as_driver_image().unwrap();
475
476                        attachment_image.flags = image.info.flags;
477                        attachment_image.usage = image.info.usage;
478                        attachment_image.width = image.info.width >> attachment.base_mip_level;
479                        attachment_image.height = image.info.height >> attachment.base_mip_level;
480                        attachment_image.layer_count = attachment.array_layer_count;
481                        attachment_image.view_formats.insert(idx, attachment.format);
482
483                        image_views[*attachment_idx as usize] = Image::view(
484                            image,
485                            ImageViewInfo {
486                                array_layer_count: attachment.array_layer_count,
487                                aspect_mask: attachment.aspect_mask,
488                                base_array_layer: attachment.base_array_layer,
489                                base_mip_level: attachment.base_mip_level,
490                                fmt: attachment.format,
491                                mip_level_count: attachment.mip_level_count,
492                                ty: image.info.ty,
493                            },
494                        )?;
495                    }
496                }
497
498                if let Some((attachment, clear_value)) = &exec.depth_stencil_clear {
499                    let attachment_idx =
500                        attachments.len() - 1 - exec.depth_stencil_resolve.is_some() as usize;
501                    let attachment_image = &mut attachments[attachment_idx];
502                    if let Err(idx) = attachment_image
503                        .view_formats
504                        .binary_search(&attachment.format)
505                    {
506                        clear_values[attachment_idx] = vk::ClearValue {
507                            depth_stencil: *clear_value,
508                        };
509
510                        let image = bindings[attachment.target].as_driver_image().unwrap();
511
512                        attachment_image.flags = image.info.flags;
513                        attachment_image.usage = image.info.usage;
514                        attachment_image.width = image.info.width >> attachment.base_mip_level;
515                        attachment_image.height = image.info.height >> attachment.base_mip_level;
516                        attachment_image.layer_count = attachment.array_layer_count;
517                        attachment_image.view_formats.insert(idx, attachment.format);
518
519                        image_views[attachment_idx] = Image::view(
520                            image,
521                            ImageViewInfo {
522                                array_layer_count: attachment.array_layer_count,
523                                aspect_mask: attachment.aspect_mask,
524                                base_array_layer: attachment.base_array_layer,
525                                base_mip_level: attachment.base_mip_level,
526                                fmt: attachment.format,
527                                mip_level_count: attachment.mip_level_count,
528                                ty: image.info.ty,
529                            },
530                        )?;
531                    }
532                }
533
534                if let Some(attachment) = exec
535                    .depth_stencil_attachment
536                    .or(exec.depth_stencil_load)
537                    .or(exec.depth_stencil_store)
538                {
539                    let attachment_idx =
540                        attachments.len() - 1 - exec.depth_stencil_resolve.is_some() as usize;
541                    let attachment_image = &mut attachments[attachment_idx];
542                    if let Err(idx) = attachment_image
543                        .view_formats
544                        .binary_search(&attachment.format)
545                    {
546                        let image = bindings[attachment.target].as_driver_image().unwrap();
547
548                        attachment_image.flags = image.info.flags;
549                        attachment_image.usage = image.info.usage;
550                        attachment_image.width = image.info.width >> attachment.base_mip_level;
551                        attachment_image.height = image.info.height >> attachment.base_mip_level;
552                        attachment_image.layer_count = attachment.array_layer_count;
553                        attachment_image.view_formats.insert(idx, attachment.format);
554
555                        image_views[attachment_idx] = Image::view(
556                            image,
557                            ImageViewInfo {
558                                array_layer_count: attachment.array_layer_count,
559                                aspect_mask: attachment.aspect_mask,
560                                base_array_layer: attachment.base_array_layer,
561                                base_mip_level: attachment.base_mip_level,
562                                fmt: attachment.format,
563                                mip_level_count: attachment.mip_level_count,
564                                ty: image.info.ty,
565                            },
566                        )?;
567                    }
568                }
569
570                if let Some(attachment) = exec
571                    .depth_stencil_resolve
572                    .map(|(attachment, ..)| attachment)
573                {
574                    let attachment_idx = attachments.len() - 1;
575                    let attachment_image = &mut attachments[attachment_idx];
576                    if let Err(idx) = attachment_image
577                        .view_formats
578                        .binary_search(&attachment.format)
579                    {
580                        let image = bindings[attachment.target].as_driver_image().unwrap();
581
582                        attachment_image.flags = image.info.flags;
583                        attachment_image.usage = image.info.usage;
584                        attachment_image.width = image.info.width >> attachment.base_mip_level;
585                        attachment_image.height = image.info.height >> attachment.base_mip_level;
586                        attachment_image.layer_count = attachment.array_layer_count;
587                        attachment_image.view_formats.insert(idx, attachment.format);
588
589                        image_views[attachment_idx] = Image::view(
590                            image,
591                            ImageViewInfo {
592                                array_layer_count: attachment.array_layer_count,
593                                aspect_mask: attachment.aspect_mask,
594                                base_array_layer: attachment.base_array_layer,
595                                base_mip_level: attachment.base_mip_level,
596                                fmt: attachment.format,
597                                mip_level_count: attachment.mip_level_count,
598                                ty: image.info.ty,
599                            },
600                        )?;
601                    }
602                }
603            }
604
605            let framebuffer =
606                RenderPass::framebuffer(render_pass, FramebufferInfo { attachments })?;
607
608            unsafe {
609                cmd_buf.device.cmd_begin_render_pass(
610                    **cmd_buf,
611                    &vk::RenderPassBeginInfo::default()
612                        .render_pass(***render_pass)
613                        .framebuffer(framebuffer)
614                        .render_area(vk::Rect2D {
615                            offset: vk::Offset2D {
616                                x: render_area.x,
617                                y: render_area.y,
618                            },
619                            extent: vk::Extent2D {
620                                width: render_area.width,
621                                height: render_area.height,
622                            },
623                        })
624                        .clear_values(clear_values)
625                        .push_next(
626                            &mut vk::RenderPassAttachmentBeginInfoKHR::default()
627                                .attachments(image_views),
628                        ),
629                    vk::SubpassContents::INLINE,
630                );
631            }
632
633            Ok(())
634        })
635    }
636
637    #[profiling::function]
638    fn bind_descriptor_sets(
639        cmd_buf: &CommandBuffer,
640        pipeline: &ExecutionPipeline,
641        physical_pass: &PhysicalPass,
642        exec_idx: usize,
643    ) {
644        if let Some(exec_descriptor_sets) = physical_pass.exec_descriptor_sets.get(&exec_idx) {
645            thread_local! {
646                static DESCRIPTOR_SETS: RefCell<Vec<vk::DescriptorSet>> = Default::default();
647            }
648
649            if exec_descriptor_sets.is_empty() {
650                return;
651            }
652
653            DESCRIPTOR_SETS.with_borrow_mut(|descriptor_sets| {
654                descriptor_sets.clear();
655                descriptor_sets.extend(
656                    exec_descriptor_sets
657                        .iter()
658                        .map(|descriptor_set| **descriptor_set),
659                );
660
661                trace!("    bind descriptor sets {:?}", descriptor_sets);
662
663                unsafe {
664                    cmd_buf.device.cmd_bind_descriptor_sets(
665                        **cmd_buf,
666                        pipeline.bind_point(),
667                        pipeline.layout(),
668                        0,
669                        descriptor_sets,
670                        &[],
671                    );
672                }
673            });
674        }
675    }
676
677    #[profiling::function]
678    fn bind_pipeline(
679        cmd_buf: &mut CommandBuffer,
680        physical_pass: &mut PhysicalPass,
681        exec_idx: usize,
682        pipeline: &mut ExecutionPipeline,
683        depth_stencil: Option<DepthStencilMode>,
684    ) -> Result<(), DriverError> {
685        if log_enabled!(Trace) {
686            let (ty, name, vk_pipeline) = match pipeline {
687                ExecutionPipeline::Compute(pipeline) => {
688                    ("compute", pipeline.name.as_ref(), ***pipeline)
689                }
690                ExecutionPipeline::Graphic(pipeline) => {
691                    ("graphic", pipeline.name.as_ref(), vk::Pipeline::null())
692                }
693                ExecutionPipeline::RayTrace(pipeline) => {
694                    ("ray trace", pipeline.name.as_ref(), ***pipeline)
695                }
696            };
697            if let Some(name) = name {
698                trace!("    bind {} pipeline {} ({:?})", ty, name, vk_pipeline);
699            } else {
700                trace!("    bind {} pipeline {:?}", ty, vk_pipeline);
701            }
702        }
703
704        // We store a shared reference to this pipeline inside the command buffer!
705        let pipeline_bind_point = pipeline.bind_point();
706        let pipeline = match pipeline {
707            ExecutionPipeline::Compute(pipeline) => ***pipeline,
708            ExecutionPipeline::Graphic(pipeline) => RenderPass::graphic_pipeline(
709                physical_pass.render_pass.as_mut().unwrap(),
710                pipeline,
711                depth_stencil,
712                exec_idx as _,
713            )?,
714            ExecutionPipeline::RayTrace(pipeline) => ***pipeline,
715        };
716
717        unsafe {
718            cmd_buf
719                .device
720                .cmd_bind_pipeline(**cmd_buf, pipeline_bind_point, pipeline);
721        }
722
723        Ok(())
724    }
725
726    fn end_render_pass(&mut self, cmd_buf: &CommandBuffer) {
727        trace!("  end render pass");
728
729        unsafe {
730            cmd_buf.device.cmd_end_render_pass(**cmd_buf);
731        }
732    }
733
734    /// Returns `true` when all recorded passes have been submitted to a driver command buffer.
735    ///
736    /// A fully-resolved graph contains no additional work and may be discarded, although doing so
737    /// will stall the GPU while the fences are waited on. It is preferrable to wait a few frame so
738    /// that the fences will have already been signalled.
739    pub fn is_resolved(&self) -> bool {
740        self.graph.passes.is_empty()
741    }
742
743    #[allow(clippy::type_complexity)]
744    #[profiling::function]
745    fn lease_descriptor_pool<P>(
746        pool: &mut P,
747        pass: &Pass,
748    ) -> Result<Option<Lease<DescriptorPool>>, DriverError>
749    where
750        P: Pool<DescriptorPoolInfo, DescriptorPool>,
751    {
752        let max_set_idx = pass
753            .execs
754            .iter()
755            .flat_map(|exec| exec.bindings.keys())
756            .map(|descriptor| descriptor.set())
757            .max()
758            .unwrap_or_default();
759        let max_sets = pass.execs.len() as u32 * (max_set_idx + 1);
760        let mut info = DescriptorPoolInfo {
761            max_sets,
762            ..Default::default()
763        };
764
765        // Find the total count of descriptors per type (there may be multiple pipelines!)
766        for pool_sizes in pass.descriptor_pools_sizes() {
767            for pool_size in pool_sizes.values() {
768                for (&descriptor_ty, &descriptor_count) in pool_size {
769                    debug_assert_ne!(descriptor_count, 0);
770
771                    match descriptor_ty {
772                        vk::DescriptorType::ACCELERATION_STRUCTURE_KHR => {
773                            info.acceleration_structure_count += descriptor_count;
774                        }
775                        vk::DescriptorType::COMBINED_IMAGE_SAMPLER => {
776                            info.combined_image_sampler_count += descriptor_count;
777                        }
778                        vk::DescriptorType::INPUT_ATTACHMENT => {
779                            info.input_attachment_count += descriptor_count;
780                        }
781                        vk::DescriptorType::SAMPLED_IMAGE => {
782                            info.sampled_image_count += descriptor_count;
783                        }
784                        vk::DescriptorType::SAMPLER => {
785                            info.sampler_count += descriptor_count;
786                        }
787                        vk::DescriptorType::STORAGE_BUFFER => {
788                            info.storage_buffer_count += descriptor_count;
789                        }
790                        vk::DescriptorType::STORAGE_BUFFER_DYNAMIC => {
791                            info.storage_buffer_dynamic_count += descriptor_count;
792                        }
793                        vk::DescriptorType::STORAGE_IMAGE => {
794                            info.storage_image_count += descriptor_count;
795                        }
796                        vk::DescriptorType::STORAGE_TEXEL_BUFFER => {
797                            info.storage_texel_buffer_count += descriptor_count;
798                        }
799                        vk::DescriptorType::UNIFORM_BUFFER => {
800                            info.uniform_buffer_count += descriptor_count;
801                        }
802                        vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC => {
803                            info.uniform_buffer_dynamic_count += descriptor_count;
804                        }
805                        vk::DescriptorType::UNIFORM_TEXEL_BUFFER => {
806                            info.uniform_texel_buffer_count += descriptor_count;
807                        }
808                        _ => unimplemented!("{descriptor_ty:?}"),
809                    };
810                }
811            }
812        }
813
814        // It's possible to execute a command-only pipeline
815        if info.is_empty() {
816            return Ok(None);
817        }
818
819        // Trivially round up the descriptor counts to increase cache coherence
820        const ATOM: u32 = 1 << 5;
821        info.acceleration_structure_count =
822            info.acceleration_structure_count.next_multiple_of(ATOM);
823        info.combined_image_sampler_count =
824            info.combined_image_sampler_count.next_multiple_of(ATOM);
825        info.input_attachment_count = info.input_attachment_count.next_multiple_of(ATOM);
826        info.sampled_image_count = info.sampled_image_count.next_multiple_of(ATOM);
827        info.sampler_count = info.sampler_count.next_multiple_of(ATOM);
828        info.storage_buffer_count = info.storage_buffer_count.next_multiple_of(ATOM);
829        info.storage_buffer_dynamic_count =
830            info.storage_buffer_dynamic_count.next_multiple_of(ATOM);
831        info.storage_image_count = info.storage_image_count.next_multiple_of(ATOM);
832        info.storage_texel_buffer_count = info.storage_texel_buffer_count.next_multiple_of(ATOM);
833        info.uniform_buffer_count = info.uniform_buffer_count.next_multiple_of(ATOM);
834        info.uniform_buffer_dynamic_count =
835            info.uniform_buffer_dynamic_count.next_multiple_of(ATOM);
836        info.uniform_texel_buffer_count = info.uniform_texel_buffer_count.next_multiple_of(ATOM);
837
838        // Notice how all sets are big enough for any other set; TODO: efficiently dont
839
840        // debug!("{:#?}", info);
841
842        Ok(Some(pool.lease(info)?))
843    }
844
845    #[profiling::function]
846    fn lease_render_pass<P>(
847        &self,
848        pool: &mut P,
849        pass_idx: usize,
850    ) -> Result<Lease<RenderPass>, DriverError>
851    where
852        P: Pool<RenderPassInfo, RenderPass>,
853    {
854        let pass = &self.graph.passes[pass_idx];
855        let (mut color_attachment_count, mut depth_stencil_attachment_count) = (0, 0);
856        for exec in &pass.execs {
857            color_attachment_count = color_attachment_count
858                .max(
859                    exec.color_attachments
860                        .keys()
861                        .max()
862                        .map(|attachment_idx| attachment_idx + 1)
863                        .unwrap_or_default() as usize,
864                )
865                .max(
866                    exec.color_clears
867                        .keys()
868                        .max()
869                        .map(|attachment_idx| attachment_idx + 1)
870                        .unwrap_or_default() as usize,
871                )
872                .max(
873                    exec.color_loads
874                        .keys()
875                        .max()
876                        .map(|attachment_idx| attachment_idx + 1)
877                        .unwrap_or_default() as usize,
878                )
879                .max(
880                    exec.color_resolves
881                        .keys()
882                        .max()
883                        .map(|attachment_idx| attachment_idx + 1)
884                        .unwrap_or_default() as usize,
885                )
886                .max(
887                    exec.color_stores
888                        .keys()
889                        .max()
890                        .map(|attachment_idx| attachment_idx + 1)
891                        .unwrap_or_default() as usize,
892                );
893            let has_depth_stencil_attachment = exec.depth_stencil_attachment.is_some()
894                || exec.depth_stencil_clear.is_some()
895                || exec.depth_stencil_load.is_some()
896                || exec.depth_stencil_store.is_some();
897            let has_depth_stencil_resolve = exec.depth_stencil_resolve.is_some();
898
899            depth_stencil_attachment_count = depth_stencil_attachment_count
900                .max(has_depth_stencil_attachment as usize + has_depth_stencil_resolve as usize);
901        }
902
903        let attachment_count = color_attachment_count + depth_stencil_attachment_count;
904        let mut attachments = Vec::with_capacity(attachment_count);
905        attachments.resize_with(attachment_count, AttachmentInfo::default);
906
907        let mut subpasses = Vec::<SubpassInfo>::with_capacity(pass.execs.len());
908
909        {
910            let mut color_set = vec![false; attachment_count];
911            let mut depth_stencil_set = false;
912
913            // Add load op attachments using the first executions
914            for exec in &pass.execs {
915                // Cleared color attachments
916                for (attachment_idx, (cleared_attachment, _)) in &exec.color_clears {
917                    let color_set = &mut color_set[*attachment_idx as usize];
918                    if *color_set {
919                        continue;
920                    }
921
922                    let attachment = &mut attachments[*attachment_idx as usize];
923                    attachment.fmt = cleared_attachment.format;
924                    attachment.sample_count = cleared_attachment.sample_count;
925                    attachment.load_op = vk::AttachmentLoadOp::CLEAR;
926                    attachment.initial_layout = vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL;
927                    *color_set = true;
928                }
929
930                // Loaded color attachments
931                for (attachment_idx, loaded_attachment) in &exec.color_loads {
932                    let color_set = &mut color_set[*attachment_idx as usize];
933                    if *color_set {
934                        continue;
935                    }
936
937                    let attachment = &mut attachments[*attachment_idx as usize];
938                    attachment.fmt = loaded_attachment.format;
939                    attachment.sample_count = loaded_attachment.sample_count;
940                    attachment.load_op = vk::AttachmentLoadOp::LOAD;
941                    attachment.initial_layout = vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL;
942                    *color_set = true;
943                }
944
945                // Cleared depth/stencil attachment
946                if !depth_stencil_set {
947                    if let Some((cleared_attachment, _)) = exec.depth_stencil_clear {
948                        let attachment = &mut attachments[color_attachment_count];
949                        attachment.fmt = cleared_attachment.format;
950                        attachment.sample_count = cleared_attachment.sample_count;
951                        attachment.initial_layout = if cleared_attachment
952                            .aspect_mask
953                            .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL)
954                        {
955                            attachment.load_op = vk::AttachmentLoadOp::CLEAR;
956                            attachment.stencil_load_op = vk::AttachmentLoadOp::CLEAR;
957
958                            vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL
959                        } else if cleared_attachment
960                            .aspect_mask
961                            .contains(vk::ImageAspectFlags::DEPTH)
962                        {
963                            attachment.load_op = vk::AttachmentLoadOp::CLEAR;
964
965                            vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL
966                        } else {
967                            attachment.stencil_load_op = vk::AttachmentLoadOp::CLEAR;
968
969                            vk::ImageLayout::STENCIL_ATTACHMENT_OPTIMAL
970                        };
971                        depth_stencil_set = true;
972                    } else if let Some(loaded_attachment) = exec.depth_stencil_load {
973                        // Loaded depth/stencil attachment
974                        let attachment = &mut attachments[color_attachment_count];
975                        attachment.fmt = loaded_attachment.format;
976                        attachment.sample_count = loaded_attachment.sample_count;
977                        attachment.initial_layout = if loaded_attachment
978                            .aspect_mask
979                            .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL)
980                        {
981                            attachment.load_op = vk::AttachmentLoadOp::LOAD;
982                            attachment.stencil_load_op = vk::AttachmentLoadOp::LOAD;
983
984                            vk::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL
985                        } else if loaded_attachment
986                            .aspect_mask
987                            .contains(vk::ImageAspectFlags::DEPTH)
988                        {
989                            attachment.load_op = vk::AttachmentLoadOp::LOAD;
990
991                            vk::ImageLayout::DEPTH_READ_ONLY_OPTIMAL
992                        } else {
993                            attachment.stencil_load_op = vk::AttachmentLoadOp::LOAD;
994
995                            vk::ImageLayout::STENCIL_READ_ONLY_OPTIMAL
996                        };
997                        depth_stencil_set = true;
998                    } else if exec.depth_stencil_clear.is_some()
999                        || exec.depth_stencil_store.is_some()
1000                    {
1001                        depth_stencil_set = true;
1002                    }
1003                }
1004            }
1005        }
1006
1007        {
1008            let mut color_set = vec![false; attachment_count];
1009            let mut depth_stencil_set = false;
1010            let mut depth_stencil_resolve_set = false;
1011
1012            // Add store op attachments using the last executions
1013            for exec in pass.execs.iter().rev() {
1014                // Resolved color attachments
1015                for (attachment_idx, (resolved_attachment, _)) in &exec.color_resolves {
1016                    let color_set = &mut color_set[*attachment_idx as usize];
1017                    if *color_set {
1018                        continue;
1019                    }
1020
1021                    let attachment = &mut attachments[*attachment_idx as usize];
1022                    attachment.fmt = resolved_attachment.format;
1023                    attachment.sample_count = resolved_attachment.sample_count;
1024                    attachment.final_layout = vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL;
1025                    *color_set = true;
1026                }
1027
1028                // Stored color attachments
1029                for (attachment_idx, stored_attachment) in &exec.color_stores {
1030                    let color_set = &mut color_set[*attachment_idx as usize];
1031                    if *color_set {
1032                        continue;
1033                    }
1034
1035                    let attachment = &mut attachments[*attachment_idx as usize];
1036                    attachment.fmt = stored_attachment.format;
1037                    attachment.sample_count = stored_attachment.sample_count;
1038                    attachment.store_op = vk::AttachmentStoreOp::STORE;
1039                    attachment.final_layout = vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL;
1040                    *color_set = true;
1041                }
1042
1043                // Stored depth/stencil attachment
1044                if !depth_stencil_set {
1045                    if let Some(stored_attachment) = exec.depth_stencil_store {
1046                        let attachment = &mut attachments[color_attachment_count];
1047                        attachment.fmt = stored_attachment.format;
1048                        attachment.sample_count = stored_attachment.sample_count;
1049                        attachment.final_layout = if stored_attachment
1050                            .aspect_mask
1051                            .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL)
1052                        {
1053                            attachment.store_op = vk::AttachmentStoreOp::STORE;
1054                            attachment.stencil_store_op = vk::AttachmentStoreOp::STORE;
1055
1056                            vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL
1057                        } else if stored_attachment
1058                            .aspect_mask
1059                            .contains(vk::ImageAspectFlags::DEPTH)
1060                        {
1061                            attachment.store_op = vk::AttachmentStoreOp::STORE;
1062
1063                            vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL
1064                        } else {
1065                            attachment.stencil_store_op = vk::AttachmentStoreOp::STORE;
1066
1067                            vk::ImageLayout::STENCIL_ATTACHMENT_OPTIMAL
1068                        };
1069                        depth_stencil_set = true;
1070                    }
1071                }
1072
1073                // Resolved depth/stencil attachment
1074                if !depth_stencil_resolve_set {
1075                    if let Some((resolved_attachment, ..)) = exec.depth_stencil_resolve {
1076                        let attachment = attachments.last_mut().unwrap();
1077                        attachment.fmt = resolved_attachment.format;
1078                        attachment.sample_count = resolved_attachment.sample_count;
1079                        attachment.final_layout = if resolved_attachment
1080                            .aspect_mask
1081                            .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL)
1082                        {
1083                            vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL
1084                        } else if resolved_attachment
1085                            .aspect_mask
1086                            .contains(vk::ImageAspectFlags::DEPTH)
1087                        {
1088                            vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL
1089                        } else {
1090                            vk::ImageLayout::STENCIL_ATTACHMENT_OPTIMAL
1091                        };
1092                        depth_stencil_resolve_set = true;
1093                    }
1094                }
1095            }
1096        }
1097
1098        for attachment in &mut attachments {
1099            if attachment.load_op == vk::AttachmentLoadOp::DONT_CARE {
1100                attachment.initial_layout = attachment.final_layout;
1101            } else if attachment.store_op == vk::AttachmentStoreOp::DONT_CARE
1102                && attachment.stencil_store_op == vk::AttachmentStoreOp::DONT_CARE
1103            {
1104                attachment.final_layout = attachment.initial_layout;
1105            }
1106        }
1107
1108        // Add subpasses
1109        for (exec_idx, exec) in pass.execs.iter().enumerate() {
1110            let pipeline = exec
1111                .pipeline
1112                .as_ref()
1113                .map(|pipeline| pipeline.unwrap_graphic())
1114                .unwrap();
1115            let mut subpass_info = SubpassInfo::with_capacity(attachment_count);
1116
1117            // Add input attachments
1118            for attachment_idx in pipeline.input_attachments.iter() {
1119                debug_assert!(
1120                    !exec.color_clears.contains_key(attachment_idx),
1121                    "cannot clear color attachment index {attachment_idx} because it uses subpass input",
1122                );
1123
1124                let exec_attachment = exec
1125                    .color_attachments
1126                    .get(attachment_idx)
1127                    .or_else(|| exec.color_loads.get(attachment_idx))
1128                    .or_else(|| exec.color_stores.get(attachment_idx))
1129                    .expect("subpass input attachment index not attached, loaded, or stored");
1130                let is_random_access = exec.color_stores.contains_key(attachment_idx);
1131                subpass_info.input_attachments.push(AttachmentRef {
1132                    attachment: *attachment_idx,
1133                    aspect_mask: exec_attachment.aspect_mask,
1134                    layout: Self::attachment_layout(
1135                        exec_attachment.aspect_mask,
1136                        is_random_access,
1137                        true,
1138                    ),
1139                });
1140
1141                // We should preserve the attachment in the previous subpasses as needed
1142                // (We're asserting that any input renderpasses are actually real subpasses
1143                // here with prior passes..)
1144                for prev_exec_idx in (0..exec_idx - 1).rev() {
1145                    let prev_exec = &pass.execs[prev_exec_idx];
1146                    if prev_exec.color_stores.contains_key(attachment_idx) {
1147                        break;
1148                    }
1149
1150                    let prev_subpass = &mut subpasses[prev_exec_idx];
1151                    prev_subpass.preserve_attachments.push(*attachment_idx);
1152                }
1153            }
1154
1155            // Set color attachments to defaults
1156            for attachment_idx in 0..color_attachment_count as u32 {
1157                let is_input = subpass_info
1158                    .input_attachments
1159                    .iter()
1160                    .any(|input| input.attachment == attachment_idx);
1161                subpass_info.color_attachments.push(AttachmentRef {
1162                    attachment: vk::ATTACHMENT_UNUSED,
1163                    aspect_mask: vk::ImageAspectFlags::COLOR,
1164                    layout: Self::attachment_layout(vk::ImageAspectFlags::COLOR, true, is_input),
1165                });
1166            }
1167
1168            for attachment_idx in exec
1169                .color_attachments
1170                .keys()
1171                .chain(exec.color_clears.keys())
1172                .chain(exec.color_loads.keys())
1173                .chain(exec.color_stores.keys())
1174            {
1175                subpass_info.color_attachments[*attachment_idx as usize].attachment =
1176                    *attachment_idx;
1177            }
1178
1179            // Set depth/stencil attachment
1180            if let Some(depth_stencil) = exec
1181                .depth_stencil_attachment
1182                .or(exec.depth_stencil_load)
1183                .or(exec.depth_stencil_store)
1184                .or_else(|| exec.depth_stencil_clear.map(|(attachment, _)| attachment))
1185            {
1186                let is_random_access = exec.depth_stencil_clear.is_some()
1187                    || exec.depth_stencil_load.is_some()
1188                    || exec.depth_stencil_store.is_some();
1189                subpass_info.depth_stencil_attachment = Some(AttachmentRef {
1190                    attachment: color_attachment_count as u32,
1191                    aspect_mask: depth_stencil.aspect_mask,
1192                    layout: Self::attachment_layout(
1193                        depth_stencil.aspect_mask,
1194                        is_random_access,
1195                        false,
1196                    ),
1197                });
1198            }
1199
1200            // Set color resolves to defaults
1201            subpass_info.color_resolve_attachments.extend(
1202                repeat(AttachmentRef {
1203                    attachment: vk::ATTACHMENT_UNUSED,
1204                    aspect_mask: vk::ImageAspectFlags::empty(),
1205                    layout: vk::ImageLayout::UNDEFINED,
1206                })
1207                .take(color_attachment_count),
1208            );
1209
1210            // Set any used color resolve attachments now
1211            for (dst_attachment_idx, (resolved_attachment, src_attachment_idx)) in
1212                &exec.color_resolves
1213            {
1214                let is_input = subpass_info
1215                    .input_attachments
1216                    .iter()
1217                    .any(|input| input.attachment == *dst_attachment_idx);
1218                subpass_info.color_resolve_attachments[*src_attachment_idx as usize] =
1219                    AttachmentRef {
1220                        attachment: *dst_attachment_idx,
1221                        aspect_mask: resolved_attachment.aspect_mask,
1222                        layout: Self::attachment_layout(
1223                            resolved_attachment.aspect_mask,
1224                            true,
1225                            is_input,
1226                        ),
1227                    };
1228            }
1229
1230            if let Some((
1231                resolved_attachment,
1232                dst_attachment_idx,
1233                depth_resolve_mode,
1234                stencil_resolve_mode,
1235            )) = exec.depth_stencil_resolve
1236            {
1237                subpass_info.depth_stencil_resolve_attachment = Some((
1238                    AttachmentRef {
1239                        attachment: dst_attachment_idx + 1,
1240                        aspect_mask: resolved_attachment.aspect_mask,
1241                        layout: Self::attachment_layout(
1242                            resolved_attachment.aspect_mask,
1243                            true,
1244                            false,
1245                        ),
1246                    },
1247                    depth_resolve_mode,
1248                    stencil_resolve_mode,
1249                ))
1250            }
1251
1252            subpass_info.view_mask = exec.view_mask;
1253            subpass_info.correlated_view_mask = exec.correlated_view_mask;
1254
1255            subpasses.push(subpass_info);
1256        }
1257
1258        // Add dependencies
1259        let dependencies =
1260            {
1261                let mut dependencies = BTreeMap::new();
1262                for (exec_idx, exec) in pass.execs.iter().enumerate() {
1263                    // Check accesses
1264                    'accesses: for (node_idx, accesses) in exec.accesses.iter() {
1265                        let (mut curr_stages, mut curr_access) =
1266                            pipeline_stage_access_flags(accesses.first().unwrap().access);
1267                        if curr_stages.contains(vk::PipelineStageFlags::ALL_COMMANDS) {
1268                            curr_stages |= vk::PipelineStageFlags::ALL_GRAPHICS;
1269                            curr_stages &= !vk::PipelineStageFlags::ALL_COMMANDS;
1270                        }
1271
1272                        // First look for through earlier executions of this pass (in reverse order)
1273                        for (prev_exec_idx, prev_exec) in
1274                            pass.execs[0..exec_idx].iter().enumerate().rev()
1275                        {
1276                            if let Some(accesses) = prev_exec.accesses.get(node_idx) {
1277                                for &SubresourceAccess { access, .. } in accesses {
1278                                    // Is this previous execution access dependent on anything the current
1279                                    // execution access is dependent upon?
1280                                    let (mut prev_stages, prev_access) =
1281                                        pipeline_stage_access_flags(access);
1282                                    if prev_stages.contains(vk::PipelineStageFlags::ALL_COMMANDS) {
1283                                        prev_stages |= vk::PipelineStageFlags::ALL_GRAPHICS;
1284                                        prev_stages &= !vk::PipelineStageFlags::ALL_COMMANDS;
1285                                    }
1286
1287                                    let common_stages = curr_stages & prev_stages;
1288                                    if common_stages.is_empty() {
1289                                        // No common dependencies
1290                                        continue;
1291                                    }
1292
1293                                    let dep = dependencies
1294                                        .entry((prev_exec_idx, exec_idx))
1295                                        .or_insert_with(|| {
1296                                            SubpassDependency::new(
1297                                                prev_exec_idx as _,
1298                                                exec_idx as _,
1299                                            )
1300                                        });
1301
1302                                    // Wait for ...
1303                                    dep.src_stage_mask |= common_stages;
1304                                    dep.src_access_mask |= prev_access;
1305
1306                                    // ... before we:
1307                                    dep.dst_stage_mask |= curr_stages;
1308                                    dep.dst_access_mask |= curr_access;
1309
1310                                    // Do the source and destination stage masks both include
1311                                    // framebuffer-space stages?
1312                                    if (prev_stages | curr_stages).intersects(
1313                                        vk::PipelineStageFlags::FRAGMENT_SHADER
1314                                            | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
1315                                            | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS
1316                                            | vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
1317                                    ) {
1318                                        dep.dependency_flags |= vk::DependencyFlags::BY_REGION;
1319                                    }
1320
1321                                    curr_stages &= !common_stages;
1322                                    curr_access &= !prev_access;
1323
1324                                    // Have we found all dependencies for this stage? If so no need to
1325                                    // check external passes
1326                                    if curr_stages.is_empty() {
1327                                        continue 'accesses;
1328                                    }
1329                                }
1330                            }
1331                        }
1332
1333                        // Second look in previous passes of the entire render graph
1334                        for prev_subpass in self.graph.passes[0..pass_idx]
1335                            .iter()
1336                            .rev()
1337                            .flat_map(|pass| pass.execs.iter().rev())
1338                        {
1339                            if let Some(accesses) = prev_subpass.accesses.get(node_idx) {
1340                                for &SubresourceAccess { access, .. } in accesses {
1341                                    // Is this previous subpass access dependent on anything the current
1342                                    // subpass access is dependent upon?
1343                                    let (prev_stages, prev_access) =
1344                                        pipeline_stage_access_flags(access);
1345                                    let common_stages = curr_stages & prev_stages;
1346                                    if common_stages.is_empty() {
1347                                        // No common dependencies
1348                                        continue;
1349                                    }
1350
1351                                    let dep = dependencies
1352                                        .entry((vk::SUBPASS_EXTERNAL as _, exec_idx))
1353                                        .or_insert_with(|| {
1354                                            SubpassDependency::new(
1355                                                vk::SUBPASS_EXTERNAL as _,
1356                                                exec_idx as _,
1357                                            )
1358                                        });
1359
1360                                    // Wait for ...
1361                                    dep.src_stage_mask |= common_stages;
1362                                    dep.src_access_mask |= prev_access;
1363
1364                                    // ... before we:
1365                                    dep.dst_stage_mask |=
1366                                        curr_stages.min(vk::PipelineStageFlags::ALL_GRAPHICS);
1367                                    dep.dst_access_mask |= curr_access;
1368
1369                                    // If the source and destination stage masks both include
1370                                    // framebuffer-space stages then we need the BY_REGION flag
1371                                    if (prev_stages | curr_stages).intersects(
1372                                        vk::PipelineStageFlags::FRAGMENT_SHADER
1373                                            | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
1374                                            | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS
1375                                            | vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
1376                                    ) {
1377                                        dep.dependency_flags |= vk::DependencyFlags::BY_REGION;
1378                                    }
1379
1380                                    curr_stages &= !common_stages;
1381                                    curr_access &= !prev_access;
1382
1383                                    // If we found all dependencies for this stage there is no need to check
1384                                    // external passes
1385                                    if curr_stages.is_empty() {
1386                                        continue 'accesses;
1387                                    }
1388                                }
1389                            }
1390                        }
1391
1392                        // Fall back to external dependencies
1393                        if !curr_stages.is_empty() {
1394                            let dep = dependencies
1395                                .entry((vk::SUBPASS_EXTERNAL as _, exec_idx))
1396                                .or_insert_with(|| {
1397                                    SubpassDependency::new(vk::SUBPASS_EXTERNAL as _, exec_idx as _)
1398                                });
1399
1400                            // Wait for ...
1401                            dep.src_stage_mask |= curr_stages;
1402                            dep.src_access_mask |= curr_access;
1403
1404                            // ... before we:
1405                            dep.dst_stage_mask |= vk::PipelineStageFlags::TOP_OF_PIPE;
1406                            dep.dst_access_mask =
1407                                vk::AccessFlags::MEMORY_READ | vk::AccessFlags::MEMORY_WRITE;
1408                        }
1409                    }
1410
1411                    // Look for attachments of this exec being read or written in other execs of the
1412                    // same pass
1413                    for (other_idx, other) in pass.execs[0..exec_idx].iter().enumerate() {
1414                        // Look for color attachments we're reading
1415                        for attachment_idx in exec.color_loads.keys() {
1416                            // Look for writes in the other exec
1417                            if other.color_clears.contains_key(attachment_idx)
1418                                || other.color_stores.contains_key(attachment_idx)
1419                                || other.color_resolves.contains_key(attachment_idx)
1420                            {
1421                                let dep = dependencies.entry((other_idx, exec_idx)).or_insert_with(
1422                                    || SubpassDependency::new(other_idx as _, exec_idx as _),
1423                                );
1424
1425                                // Wait for ...
1426                                dep.src_stage_mask |=
1427                                    vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT;
1428                                dep.src_access_mask |= vk::AccessFlags::COLOR_ATTACHMENT_WRITE;
1429
1430                                // ... before we:
1431                                dep.dst_stage_mask |= vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS;
1432                                dep.dst_access_mask |= vk::AccessFlags::COLOR_ATTACHMENT_READ;
1433                            }
1434
1435                            // look for reads in the other exec
1436                            if other.color_loads.contains_key(attachment_idx) {
1437                                let dep = dependencies.entry((other_idx, exec_idx)).or_insert_with(
1438                                    || SubpassDependency::new(other_idx as _, exec_idx as _),
1439                                );
1440
1441                                // Wait for ...
1442                                dep.src_stage_mask |= vk::PipelineStageFlags::LATE_FRAGMENT_TESTS;
1443                                dep.src_access_mask |= vk::AccessFlags::COLOR_ATTACHMENT_READ;
1444
1445                                // ... before we:
1446                                dep.dst_stage_mask |= vk::PipelineStageFlags::FRAGMENT_SHADER;
1447                                dep.dst_access_mask |= vk::AccessFlags::COLOR_ATTACHMENT_READ;
1448                            }
1449                        }
1450
1451                        // Look for a depth/stencil attachment read
1452                        if exec.depth_stencil_load.is_some() {
1453                            // Look for writes in the other exec
1454                            if other.depth_stencil_clear.is_some()
1455                                || other.depth_stencil_store.is_some()
1456                                || other.depth_stencil_resolve.is_some()
1457                            {
1458                                let dep = dependencies.entry((other_idx, exec_idx)).or_insert_with(
1459                                    || SubpassDependency::new(other_idx as _, exec_idx as _),
1460                                );
1461
1462                                // Wait for ...
1463                                dep.src_stage_mask |= vk::PipelineStageFlags::LATE_FRAGMENT_TESTS;
1464                                dep.src_access_mask |=
1465                                    vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE;
1466
1467                                // ... before we:
1468                                dep.dst_stage_mask |= vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS;
1469                                dep.dst_access_mask |=
1470                                    vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ;
1471                            }
1472
1473                            // TODO: Do we need to depend on a READ..READ between subpasses?
1474                            // look for reads in the other exec
1475                            if other.depth_stencil_load.is_some() {
1476                                let dep = dependencies.entry((other_idx, exec_idx)).or_insert_with(
1477                                    || SubpassDependency::new(other_idx as _, exec_idx as _),
1478                                );
1479
1480                                // Wait for ...
1481                                dep.src_stage_mask |= vk::PipelineStageFlags::LATE_FRAGMENT_TESTS;
1482                                dep.src_access_mask |=
1483                                    vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ;
1484
1485                                // ... before we:
1486                                dep.dst_stage_mask |= vk::PipelineStageFlags::FRAGMENT_SHADER;
1487                                dep.dst_access_mask |=
1488                                    vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ;
1489                            }
1490                        }
1491
1492                        // Look for color attachments we're writing
1493                        for (attachment_idx, aspect_mask) in
1494                            exec.color_clears
1495                                .iter()
1496                                .map(|(attachment_idx, (attachment, _))| {
1497                                    (*attachment_idx, attachment.aspect_mask)
1498                                })
1499                                .chain(exec.color_resolves.iter().map(
1500                                    |(dst_attachment_idx, (resolved_attachment, _))| {
1501                                        (*dst_attachment_idx, resolved_attachment.aspect_mask)
1502                                    },
1503                                ))
1504                                .chain(exec.color_stores.iter().map(
1505                                    |(attachment_idx, attachment)| {
1506                                        (*attachment_idx, attachment.aspect_mask)
1507                                    },
1508                                ))
1509                        {
1510                            let stage = match aspect_mask {
1511                                mask if mask.contains(vk::ImageAspectFlags::COLOR) => {
1512                                    vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
1513                                }
1514                                mask if mask.intersects(
1515                                    vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL,
1516                                ) =>
1517                                {
1518                                    vk::PipelineStageFlags::LATE_FRAGMENT_TESTS
1519                                }
1520                                _ => vk::PipelineStageFlags::ALL_GRAPHICS,
1521                            };
1522
1523                            // Look for writes in the other exec
1524                            if other.color_clears.contains_key(&attachment_idx)
1525                                || other.color_stores.contains_key(&attachment_idx)
1526                                || other.color_resolves.contains_key(&attachment_idx)
1527                            {
1528                                let access = match aspect_mask {
1529                                    mask if mask.contains(vk::ImageAspectFlags::COLOR) => {
1530                                        vk::AccessFlags::COLOR_ATTACHMENT_WRITE
1531                                    }
1532                                    mask if mask.intersects(
1533                                        vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL,
1534                                    ) =>
1535                                    {
1536                                        vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE
1537                                    }
1538                                    _ => {
1539                                        vk::AccessFlags::MEMORY_READ | vk::AccessFlags::MEMORY_WRITE
1540                                    }
1541                                };
1542
1543                                let dep = dependencies.entry((other_idx, exec_idx)).or_insert_with(
1544                                    || SubpassDependency::new(other_idx as _, exec_idx as _),
1545                                );
1546
1547                                // Wait for ...
1548                                dep.src_stage_mask |= stage;
1549                                dep.src_access_mask |= access;
1550
1551                                // ... before we:
1552                                dep.dst_stage_mask |= stage;
1553                                dep.dst_access_mask |= access;
1554                            }
1555
1556                            // look for reads in the other exec
1557                            if other.color_loads.contains_key(&attachment_idx) {
1558                                let (src_access, dst_access) = match aspect_mask {
1559                                    mask if mask.contains(vk::ImageAspectFlags::COLOR) => (
1560                                        vk::AccessFlags::COLOR_ATTACHMENT_READ,
1561                                        vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
1562                                    ),
1563                                    mask if mask.intersects(
1564                                        vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL,
1565                                    ) =>
1566                                    {
1567                                        (
1568                                            vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ,
1569                                            vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
1570                                        )
1571                                    }
1572                                    _ => (
1573                                        vk::AccessFlags::MEMORY_READ
1574                                            | vk::AccessFlags::MEMORY_WRITE,
1575                                        vk::AccessFlags::MEMORY_READ
1576                                            | vk::AccessFlags::MEMORY_WRITE,
1577                                    ),
1578                                };
1579
1580                                let dep = dependencies.entry((other_idx, exec_idx)).or_insert_with(
1581                                    || SubpassDependency::new(other_idx as _, exec_idx as _),
1582                                );
1583
1584                                // Wait for ...
1585                                dep.src_stage_mask |= vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS;
1586                                dep.src_access_mask |= src_access;
1587
1588                                // ... before we:
1589                                dep.dst_stage_mask |= stage;
1590                                dep.dst_access_mask |= dst_access;
1591                            }
1592                        }
1593
1594                        // Look for a depth/stencil attachment write
1595                        if let Some(aspect_mask) = exec
1596                            .depth_stencil_clear
1597                            .map(|(attachment, _)| attachment.aspect_mask)
1598                            .or_else(|| {
1599                                exec.depth_stencil_store
1600                                    .map(|attachment| attachment.aspect_mask)
1601                            })
1602                            .or_else(|| {
1603                                exec.depth_stencil_resolve
1604                                    .map(|(attachment, ..)| attachment.aspect_mask)
1605                            })
1606                        {
1607                            let stage = match aspect_mask {
1608                                mask if mask.contains(vk::ImageAspectFlags::COLOR) => {
1609                                    vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
1610                                }
1611                                mask if mask.intersects(
1612                                    vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL,
1613                                ) =>
1614                                {
1615                                    vk::PipelineStageFlags::LATE_FRAGMENT_TESTS
1616                                }
1617                                _ => vk::PipelineStageFlags::ALL_GRAPHICS,
1618                            };
1619
1620                            // Look for writes in the other exec
1621                            if other.depth_stencil_clear.is_some()
1622                                || other.depth_stencil_store.is_some()
1623                                || other.depth_stencil_resolve.is_some()
1624                            {
1625                                let access = match aspect_mask {
1626                                    mask if mask.contains(vk::ImageAspectFlags::COLOR) => {
1627                                        vk::AccessFlags::COLOR_ATTACHMENT_WRITE
1628                                    }
1629                                    mask if mask.intersects(
1630                                        vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL,
1631                                    ) =>
1632                                    {
1633                                        vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE
1634                                    }
1635                                    _ => {
1636                                        vk::AccessFlags::MEMORY_READ | vk::AccessFlags::MEMORY_WRITE
1637                                    }
1638                                };
1639
1640                                let dep = dependencies.entry((other_idx, exec_idx)).or_insert_with(
1641                                    || SubpassDependency::new(other_idx as _, exec_idx as _),
1642                                );
1643
1644                                // Wait for ...
1645                                dep.src_stage_mask |= stage;
1646                                dep.src_access_mask |= access;
1647
1648                                // ... before we:
1649                                dep.dst_stage_mask |= stage;
1650                                dep.dst_access_mask |= access;
1651                            }
1652
1653                            // look for reads in the other exec
1654                            if other.depth_stencil_load.is_some() {
1655                                let (src_access, dst_access) = match aspect_mask {
1656                                    mask if mask.contains(vk::ImageAspectFlags::COLOR) => (
1657                                        vk::AccessFlags::COLOR_ATTACHMENT_READ,
1658                                        vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
1659                                    ),
1660                                    mask if mask.intersects(
1661                                        vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL,
1662                                    ) =>
1663                                    {
1664                                        (
1665                                            vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ,
1666                                            vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
1667                                        )
1668                                    }
1669                                    _ => (
1670                                        vk::AccessFlags::MEMORY_READ
1671                                            | vk::AccessFlags::MEMORY_WRITE,
1672                                        vk::AccessFlags::MEMORY_READ
1673                                            | vk::AccessFlags::MEMORY_WRITE,
1674                                    ),
1675                                };
1676
1677                                let dep = dependencies.entry((other_idx, exec_idx)).or_insert_with(
1678                                    || SubpassDependency::new(other_idx as _, exec_idx as _),
1679                                );
1680
1681                                // Wait for ...
1682                                dep.src_stage_mask |= vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS;
1683                                dep.src_access_mask |= src_access;
1684
1685                                // ... before we:
1686                                dep.dst_stage_mask |= stage;
1687                                dep.dst_access_mask |= dst_access;
1688                            }
1689                        }
1690                    }
1691                }
1692
1693                dependencies.into_values().collect::<Vec<_>>()
1694            };
1695
1696        // let info = RenderPassInfo {
1697        //     attachments,
1698        //     dependencies,
1699        //     subpasses,
1700        // };
1701
1702        // trace!("{:#?}", info);
1703
1704        pool.lease(RenderPassInfo {
1705            attachments,
1706            dependencies,
1707            subpasses,
1708        })
1709    }
1710
1711    #[profiling::function]
1712    fn lease_scheduled_resources<P>(
1713        &mut self,
1714        pool: &mut P,
1715        schedule: &[usize],
1716    ) -> Result<(), DriverError>
1717    where
1718        P: Pool<DescriptorPoolInfo, DescriptorPool> + Pool<RenderPassInfo, RenderPass>,
1719    {
1720        for pass_idx in schedule.iter().copied() {
1721            // At the time this function runs the pass will already have been optimized into a
1722            // larger pass made out of anything that might have been merged into it - so we
1723            // only care about one pass at a time here
1724            let pass = &mut self.graph.passes[pass_idx];
1725
1726            trace!("leasing [{pass_idx}: {}]", pass.name);
1727
1728            let descriptor_pool = Self::lease_descriptor_pool(pool, pass)?;
1729            let mut exec_descriptor_sets = HashMap::with_capacity(
1730                descriptor_pool
1731                    .as_ref()
1732                    .map(|descriptor_pool| descriptor_pool.info.max_sets as usize)
1733                    .unwrap_or_default(),
1734            );
1735            if let Some(descriptor_pool) = descriptor_pool.as_ref() {
1736                for (exec_idx, pipeline) in
1737                    pass.execs
1738                        .iter()
1739                        .enumerate()
1740                        .filter_map(|(exec_idx, exec)| {
1741                            exec.pipeline.as_ref().map(|pipeline| (exec_idx, pipeline))
1742                        })
1743                {
1744                    let layouts = pipeline.descriptor_info().layouts.values();
1745                    let mut descriptor_sets = Vec::with_capacity(layouts.len());
1746                    for descriptor_set_layout in layouts {
1747                        descriptor_sets.push(DescriptorPool::allocate_descriptor_set(
1748                            descriptor_pool,
1749                            descriptor_set_layout,
1750                        )?);
1751                    }
1752                    exec_descriptor_sets.insert(exec_idx, descriptor_sets);
1753                }
1754            }
1755
1756            // Note that as a side effect of merging compatible passes all input passes should
1757            // be globbed onto their preceeding passes by now. This allows subpasses to use
1758            // input attachments without really doing anything, so we are provided a pass that
1759            // starts with input we just blow up b/c we can't provide it, or at least shouldn't.
1760            debug_assert!(!pass.execs.is_empty());
1761            debug_assert!(
1762                pass.execs[0].pipeline.is_none()
1763                    || !pass.execs[0].pipeline.as_ref().unwrap().is_graphic()
1764                    || pass.execs[0]
1765                        .pipeline
1766                        .as_ref()
1767                        .unwrap()
1768                        .unwrap_graphic()
1769                        .descriptor_info
1770                        .pool_sizes
1771                        .values()
1772                        .filter_map(|pool| pool.get(&vk::DescriptorType::INPUT_ATTACHMENT))
1773                        .next()
1774                        .is_none()
1775            );
1776
1777            // Also the renderpass may just be None if the pass contained no graphic ops.
1778            let render_pass = if pass.execs[0]
1779                .pipeline
1780                .as_ref()
1781                .map(|pipeline| pipeline.is_graphic())
1782                .unwrap_or_default()
1783            {
1784                Some(self.lease_render_pass(pool, pass_idx)?)
1785            } else {
1786                None
1787            };
1788
1789            self.physical_passes.push(PhysicalPass {
1790                descriptor_pool,
1791                exec_descriptor_sets,
1792                render_pass,
1793            });
1794        }
1795
1796        Ok(())
1797    }
1798
1799    // Merges passes which are graphic with common-ish attachments - note that scheduled pass order
1800    // is final during this function and so we must merge contiguous groups of passes
1801    #[profiling::function]
1802    fn merge_scheduled_passes(&mut self, schedule: &mut Vec<usize>) {
1803        thread_local! {
1804            static PASSES: RefCell<Vec<Option<Pass>>> = Default::default();
1805        }
1806
1807        PASSES.with_borrow_mut(|passes| {
1808            debug_assert!(passes.is_empty());
1809
1810            passes.extend(self.graph.passes.drain(..).map(Some));
1811
1812            let mut idx = 0;
1813
1814            // debug!("attempting to merge {} passes", schedule.len(),);
1815
1816            while idx < schedule.len() {
1817                let mut pass = passes[schedule[idx]].take().unwrap();
1818
1819                // Find candidates
1820                let start = idx + 1;
1821                let mut end = start;
1822                while end < schedule.len() {
1823                    let other = passes[schedule[end]].as_ref().unwrap();
1824
1825                    debug!(
1826                        "attempting to merge [{idx}: {}] with [{end}: {}]",
1827                        pass.name, other.name
1828                    );
1829
1830                    if Self::allow_merge_passes(&pass, other) {
1831                        end += 1;
1832                    } else {
1833                        break;
1834                    }
1835                }
1836
1837                if log_enabled!(Trace) && start != end {
1838                    trace!("merging {} passes into [{idx}: {}]", end - start, pass.name);
1839                }
1840
1841                // Grow the merged pass once, not per merge
1842                {
1843                    let mut name_additional = 0;
1844                    let mut execs_additional = 0;
1845                    for idx in start..end {
1846                        let other = passes[schedule[idx]].as_ref().unwrap();
1847                        name_additional += other.name.len() + 3;
1848                        execs_additional += other.execs.len();
1849                    }
1850
1851                    pass.name.reserve(name_additional);
1852                    pass.execs.reserve(execs_additional);
1853                }
1854
1855                for idx in start..end {
1856                    let mut other = passes[schedule[idx]].take().unwrap();
1857                    pass.name.push_str(" + ");
1858                    pass.name.push_str(other.name.as_str());
1859                    pass.execs.append(&mut other.execs);
1860                }
1861
1862                self.graph.passes.push(pass);
1863                idx += 1 + end - start;
1864            }
1865
1866            // Reschedule passes
1867            schedule.truncate(self.graph.passes.len());
1868
1869            for (idx, pass_idx) in schedule.iter_mut().enumerate() {
1870                *pass_idx = idx;
1871            }
1872
1873            // Add the remaining passes back into the graph for later
1874            for pass in passes.drain(..).flatten() {
1875                self.graph.passes.push(pass);
1876            }
1877        });
1878    }
1879
1880    fn next_subpass(cmd_buf: &CommandBuffer) {
1881        trace!("next_subpass");
1882
1883        unsafe {
1884            cmd_buf
1885                .device
1886                .cmd_next_subpass(**cmd_buf, vk::SubpassContents::INLINE);
1887        }
1888    }
1889
1890    /// Returns the stages that process the given node.
1891    ///
1892    /// Note that this value must be retrieved before resolving a node as there will be no
1893    /// data left to inspect afterwards!
1894    #[profiling::function]
1895    pub fn node_pipeline_stages(&self, node: impl Node) -> vk::PipelineStageFlags {
1896        let node_idx = node.index();
1897        let mut res = Default::default();
1898
1899        'pass: for pass in self.graph.passes.iter() {
1900            for exec in pass.execs.iter() {
1901                if exec.accesses.contains_key(&node_idx) {
1902                    res |= pass
1903                        .execs
1904                        .iter()
1905                        .filter_map(|exec| exec.pipeline.as_ref())
1906                        .map(|pipeline| pipeline.stage())
1907                        .reduce(|j, k| j | k)
1908                        .unwrap_or(vk::PipelineStageFlags::TRANSFER);
1909
1910                    // The execution pipelines of a pass are always the same type
1911                    continue 'pass;
1912                }
1913            }
1914        }
1915
1916        debug_assert_ne!(
1917            res,
1918            Default::default(),
1919            "The given node was not accessed in this graph"
1920        );
1921
1922        res
1923    }
1924
1925    #[profiling::function]
1926    fn record_execution_barriers<'a>(
1927        cmd_buf: &CommandBuffer,
1928        bindings: &mut [Binding],
1929        accesses: impl Iterator<Item = (&'a NodeIndex, &'a Vec<SubresourceAccess>)>,
1930    ) {
1931        use std::slice::from_ref;
1932
1933        // We store a Barriers in TLS to save an alloc; contents are POD
1934        thread_local! {
1935            static TLS: RefCell<Tls> = Default::default();
1936        }
1937
1938        struct Barrier<T> {
1939            next_access: AccessType,
1940            prev_access: AccessType,
1941            resource: T,
1942        }
1943
1944        struct BufferResource {
1945            buffer: vk::Buffer,
1946            offset: usize,
1947            size: usize,
1948        }
1949
1950        struct ImageResource {
1951            image: vk::Image,
1952            range: vk::ImageSubresourceRange,
1953        }
1954
1955        #[derive(Default)]
1956        struct Tls {
1957            buffers: Vec<Barrier<BufferResource>>,
1958            images: Vec<Barrier<ImageResource>>,
1959            next_accesses: Vec<AccessType>,
1960            prev_accesses: Vec<AccessType>,
1961        }
1962
1963        TLS.with_borrow_mut(|tls| {
1964            // Initialize TLS from a previous call
1965            tls.buffers.clear();
1966            tls.images.clear();
1967            tls.next_accesses.clear();
1968            tls.prev_accesses.clear();
1969
1970            // Map remaining accesses into vk_sync barriers (some accesses may have been removed by the
1971            // render pass leasing function)
1972
1973            for (node_idx, accesses) in accesses {
1974                let binding = &bindings[*node_idx];
1975
1976                match binding {
1977                    Binding::AccelerationStructure(..)
1978                    | Binding::AccelerationStructureLease(..) => {
1979                        let Some(accel_struct) = binding.as_driver_acceleration_structure() else {
1980                            #[cfg(debug_assertions)]
1981                            unreachable!();
1982
1983                            #[cfg(not(debug_assertions))]
1984                            unsafe {
1985                                unreachable_unchecked()
1986                            }
1987                        };
1988
1989                        let prev_access = AccelerationStructure::access(
1990                            accel_struct,
1991                            accesses.last().unwrap().access,
1992                        );
1993
1994                        tls.next_accesses.extend(
1995                            accesses
1996                                .iter()
1997                                .map(|&SubresourceAccess { access, .. }| access),
1998                        );
1999                        tls.prev_accesses.push(prev_access);
2000                    }
2001                    Binding::Buffer(..) | Binding::BufferLease(..) => {
2002                        let Some(buffer) = binding.as_driver_buffer() else {
2003                            #[cfg(debug_assertions)]
2004                            unreachable!();
2005
2006                            #[cfg(not(debug_assertions))]
2007                            unsafe {
2008                                unreachable_unchecked()
2009                            }
2010                        };
2011
2012                        for &SubresourceAccess {
2013                            access,
2014                            subresource,
2015                        } in accesses
2016                        {
2017                            let Subresource::Buffer(range) = subresource else {
2018                                unreachable!()
2019                            };
2020
2021                            for (prev_access, range) in Buffer::access(buffer, access, range) {
2022                                tls.buffers.push(Barrier {
2023                                    next_access: access,
2024                                    prev_access,
2025                                    resource: BufferResource {
2026                                        buffer: **buffer,
2027                                        offset: range.start as _,
2028                                        size: (range.end - range.start) as _,
2029                                    },
2030                                });
2031                            }
2032                        }
2033                    }
2034                    Binding::Image(..) | Binding::ImageLease(..) | Binding::SwapchainImage(..) => {
2035                        let Some(image) = binding.as_driver_image() else {
2036                            #[cfg(debug_assertions)]
2037                            unreachable!();
2038
2039                            #[cfg(not(debug_assertions))]
2040                            unsafe {
2041                                unreachable_unchecked()
2042                            }
2043                        };
2044
2045                        for &SubresourceAccess {
2046                            access,
2047                            subresource,
2048                        } in accesses
2049                        {
2050                            let Subresource::Image(range) = subresource else {
2051                                unreachable!()
2052                            };
2053
2054                            for (prev_access, range) in Image::access(image, access, range) {
2055                                tls.images.push(Barrier {
2056                                    next_access: access,
2057                                    prev_access,
2058                                    resource: ImageResource {
2059                                        image: **image,
2060                                        range,
2061                                    },
2062                                })
2063                            }
2064                        }
2065                    }
2066                }
2067            }
2068
2069            let global_barrier = if !tls.next_accesses.is_empty() {
2070                // No resource attached - we use a global barrier for these
2071                trace!(
2072                    "    global {:?}->{:?}",
2073                    tls.next_accesses, tls.prev_accesses
2074                );
2075
2076                Some(GlobalBarrier {
2077                    next_accesses: tls.next_accesses.as_slice(),
2078                    previous_accesses: tls.prev_accesses.as_slice(),
2079                })
2080            } else {
2081                None
2082            };
2083            let buffer_barriers = tls.buffers.iter().map(
2084                |Barrier {
2085                     next_access,
2086                     prev_access,
2087                     resource,
2088                 }| {
2089                    let BufferResource {
2090                        buffer,
2091                        offset,
2092                        size,
2093                    } = *resource;
2094
2095                    trace!(
2096                        "    buffer {:?} {:?} {:?}->{:?}",
2097                        buffer,
2098                        offset..offset + size,
2099                        prev_access,
2100                        next_access,
2101                    );
2102
2103                    BufferBarrier {
2104                        next_accesses: from_ref(next_access),
2105                        previous_accesses: from_ref(prev_access),
2106                        src_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
2107                        dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
2108                        buffer,
2109                        offset,
2110                        size,
2111                    }
2112                },
2113            );
2114            let image_barriers = tls.images.iter().map(
2115                |Barrier {
2116                     next_access,
2117                     prev_access,
2118                     resource,
2119                 }| {
2120                    let ImageResource { image, range } = *resource;
2121
2122                    struct ImageSubresourceRangeDebug(vk::ImageSubresourceRange);
2123
2124                    impl std::fmt::Debug for ImageSubresourceRangeDebug {
2125                        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2126                            self.0.aspect_mask.fmt(f)?;
2127
2128                            f.write_str(" array: ")?;
2129
2130                            let array_layers = self.0.base_array_layer
2131                                ..self.0.base_array_layer + self.0.layer_count;
2132                            array_layers.fmt(f)?;
2133
2134                            f.write_str(" mip: ")?;
2135
2136                            let mip_levels =
2137                                self.0.base_mip_level..self.0.base_mip_level + self.0.level_count;
2138                            mip_levels.fmt(f)
2139                        }
2140                    }
2141
2142                    trace!(
2143                        "    image {:?} {:?} {:?}->{:?}",
2144                        image,
2145                        ImageSubresourceRangeDebug(range),
2146                        prev_access,
2147                        next_access,
2148                    );
2149
2150                    ImageBarrier {
2151                        next_accesses: from_ref(next_access),
2152                        next_layout: image_access_layout(*next_access),
2153                        previous_accesses: from_ref(prev_access),
2154                        previous_layout: image_access_layout(*prev_access),
2155                        discard_contents: *prev_access == AccessType::Nothing
2156                            || is_write_access(*next_access),
2157                        src_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
2158                        dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
2159                        image,
2160                        range,
2161                    }
2162                },
2163            );
2164
2165            pipeline_barrier(
2166                &cmd_buf.device,
2167                **cmd_buf,
2168                global_barrier,
2169                &buffer_barriers.collect::<Box<[_]>>(),
2170                &image_barriers.collect::<Box<[_]>>(),
2171            );
2172        });
2173    }
2174
2175    #[profiling::function]
2176    fn record_image_layout_transitions(
2177        cmd_buf: &CommandBuffer,
2178        bindings: &mut [Binding],
2179        pass: &mut Pass,
2180    ) {
2181        use std::slice::from_ref;
2182
2183        // We store a Barriers in TLS to save an alloc; contents are POD
2184        thread_local! {
2185            static TLS: RefCell<Tls> = Default::default();
2186        }
2187
2188        struct ImageResourceBarrier {
2189            image: vk::Image,
2190            next_access: AccessType,
2191            prev_access: AccessType,
2192            range: vk::ImageSubresourceRange,
2193        }
2194
2195        #[derive(Default)]
2196        struct Tls {
2197            images: Vec<ImageResourceBarrier>,
2198            initial_layouts: HashMap<usize, ImageAccess<bool>>,
2199        }
2200
2201        TLS.with_borrow_mut(|tls| {
2202            tls.images.clear();
2203            tls.initial_layouts.clear();
2204
2205            for (node_idx, accesses) in pass
2206                .execs
2207                .iter_mut()
2208                .flat_map(|exec| exec.accesses.iter())
2209                .map(|(node_idx, accesses)| (*node_idx, accesses))
2210            {
2211                debug_assert!(bindings.get(node_idx).is_some());
2212
2213                let binding = unsafe {
2214                    // PassRef enforces this using assert_bound_graph_node
2215                    bindings.get_unchecked(node_idx)
2216                };
2217
2218                match binding {
2219                    Binding::AccelerationStructure(..)
2220                    | Binding::AccelerationStructureLease(..) => {
2221                        let Some(accel_struct) = binding.as_driver_acceleration_structure() else {
2222                            #[cfg(debug_assertions)]
2223                            unreachable!();
2224
2225                            #[cfg(not(debug_assertions))]
2226                            unsafe {
2227                                unreachable_unchecked()
2228                            }
2229                        };
2230
2231                        AccelerationStructure::access(accel_struct, AccessType::Nothing);
2232                    }
2233                    Binding::Buffer(..) | Binding::BufferLease(..) => {
2234                        let Some(buffer) = binding.as_driver_buffer() else {
2235                            #[cfg(debug_assertions)]
2236                            unreachable!();
2237
2238                            #[cfg(not(debug_assertions))]
2239                            unsafe {
2240                                unreachable_unchecked()
2241                            }
2242                        };
2243
2244                        for subresource_access in accesses {
2245                            let &SubresourceAccess {
2246                                subresource: Subresource::Buffer(access_range),
2247                                ..
2248                            } = subresource_access
2249                            else {
2250                                #[cfg(debug_assertions)]
2251                                unreachable!();
2252
2253                                #[cfg(not(debug_assertions))]
2254                                unsafe {
2255                                    // This cannot be reached because PassRef enforces the subrange is
2256                                    // of type N::Subresource where N is the image node type
2257                                    unreachable_unchecked()
2258                                }
2259                            };
2260
2261                            for _ in Buffer::access(buffer, AccessType::Nothing, access_range) {}
2262                        }
2263                    }
2264                    Binding::Image(..) | Binding::ImageLease(..) | Binding::SwapchainImage(..) => {
2265                        let Some(image) = binding.as_driver_image() else {
2266                            #[cfg(debug_assertions)]
2267                            unreachable!();
2268
2269                            #[cfg(not(debug_assertions))]
2270                            unsafe {
2271                                unreachable_unchecked()
2272                            }
2273                        };
2274
2275                        let initial_layout = tls
2276                            .initial_layouts
2277                            .entry(node_idx)
2278                            .or_insert_with(|| ImageAccess::new(image.info, true));
2279
2280                        for subresource_access in accesses {
2281                            let &SubresourceAccess {
2282                                access,
2283                                subresource: Subresource::Image(access_range),
2284                            } = subresource_access
2285                            else {
2286                                #[cfg(debug_assertions)]
2287                                unreachable!();
2288
2289                                #[cfg(not(debug_assertions))]
2290                                unsafe {
2291                                    // This cannot be reached because PassRef enforces the subrange is
2292                                    // of type N::Subresource where N is the image node type
2293                                    unreachable_unchecked()
2294                                }
2295                            };
2296
2297                            for (initial_layout, layout_range) in
2298                                initial_layout.access(false, access_range)
2299                            {
2300                                for (prev_access, range) in
2301                                    Image::access(image, access, layout_range)
2302                                {
2303                                    if initial_layout {
2304                                        tls.images.push(ImageResourceBarrier {
2305                                            image: **image,
2306                                            next_access: initial_image_layout_access(access),
2307                                            prev_access,
2308                                            range,
2309                                        });
2310                                    }
2311                                }
2312                            }
2313                        }
2314                    }
2315                }
2316            }
2317
2318            let image_barriers = tls.images.iter().map(
2319                |ImageResourceBarrier {
2320                     image,
2321                     next_access,
2322                     prev_access,
2323                     range,
2324                 }| {
2325                    trace!(
2326                        "    image {:?} {:?} {:?}->{:?}",
2327                        image,
2328                        ImageSubresourceRangeDebug(*range),
2329                        prev_access,
2330                        next_access,
2331                    );
2332
2333                    ImageBarrier {
2334                        next_accesses: from_ref(next_access),
2335                        next_layout: image_access_layout(*next_access),
2336                        previous_accesses: from_ref(prev_access),
2337                        previous_layout: image_access_layout(*prev_access),
2338                        discard_contents: *prev_access == AccessType::Nothing
2339                            || is_write_access(*next_access),
2340                        src_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
2341                        dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
2342                        image: *image,
2343                        range: *range,
2344                    }
2345                },
2346            );
2347
2348            pipeline_barrier(
2349                &cmd_buf.device,
2350                **cmd_buf,
2351                None,
2352                &[],
2353                &image_barriers.collect::<Box<_>>(),
2354            );
2355        });
2356    }
2357
2358    /// Records any pending render graph passes that are required by the given node, but does not
2359    /// record any passes that actually contain the given node.
2360    ///
2361    /// As a side effect, the graph is optimized for the given node. Future calls may further optimize
2362    /// the graph, but only on top of the existing optimizations. This only matters if you are pulling
2363    /// multiple images out and you care - in that case pull the "most important" image first.
2364    #[profiling::function]
2365    pub fn record_node_dependencies<P>(
2366        &mut self,
2367        pool: &mut P,
2368        cmd_buf: &mut CommandBuffer,
2369        node: impl Node,
2370    ) -> Result<(), DriverError>
2371    where
2372        P: Pool<DescriptorPoolInfo, DescriptorPool> + Pool<RenderPassInfo, RenderPass>,
2373    {
2374        let node_idx = node.index();
2375
2376        debug_assert!(self.graph.bindings.get(node_idx).is_some());
2377
2378        // We record up to but not including the first pass which accesses the target node
2379        if let Some(end_pass_idx) = self.graph.first_node_access_pass_index(node) {
2380            self.record_node_passes(pool, cmd_buf, node_idx, end_pass_idx)?;
2381        }
2382
2383        Ok(())
2384    }
2385
2386    /// Records any pending render graph passes that the given node requires.
2387    #[profiling::function]
2388    pub fn record_node<P>(
2389        &mut self,
2390        pool: &mut P,
2391        cmd_buf: &mut CommandBuffer,
2392        node: impl Node,
2393    ) -> Result<(), DriverError>
2394    where
2395        P: Pool<DescriptorPoolInfo, DescriptorPool> + Pool<RenderPassInfo, RenderPass>,
2396    {
2397        let node_idx = node.index();
2398
2399        debug_assert!(self.graph.bindings.get(node_idx).is_some());
2400
2401        if self.graph.passes.is_empty() {
2402            return Ok(());
2403        }
2404
2405        let end_pass_idx = self.graph.passes.len();
2406        self.record_node_passes(pool, cmd_buf, node_idx, end_pass_idx)
2407    }
2408
2409    #[profiling::function]
2410    fn record_node_passes<P>(
2411        &mut self,
2412        pool: &mut P,
2413        cmd_buf: &mut CommandBuffer,
2414        node_idx: usize,
2415        end_pass_idx: usize,
2416    ) -> Result<(), DriverError>
2417    where
2418        P: Pool<DescriptorPoolInfo, DescriptorPool> + Pool<RenderPassInfo, RenderPass>,
2419    {
2420        thread_local! {
2421            static SCHEDULE: RefCell<Schedule> = Default::default();
2422        }
2423
2424        SCHEDULE.with_borrow_mut(|schedule| {
2425            schedule.access_cache.update(&self.graph, end_pass_idx);
2426            schedule.passes.clear();
2427
2428            self.schedule_node_passes(node_idx, end_pass_idx, schedule);
2429            self.record_scheduled_passes(pool, cmd_buf, schedule, end_pass_idx)
2430        })
2431    }
2432
2433    #[profiling::function]
2434    fn record_scheduled_passes<P>(
2435        &mut self,
2436        pool: &mut P,
2437        cmd_buf: &mut CommandBuffer,
2438        schedule: &mut Schedule,
2439        end_pass_idx: usize,
2440    ) -> Result<(), DriverError>
2441    where
2442        P: Pool<DescriptorPoolInfo, DescriptorPool> + Pool<RenderPassInfo, RenderPass>,
2443    {
2444        if schedule.passes.is_empty() {
2445            return Ok(());
2446        }
2447
2448        // Print some handy details or hit a breakpoint if you set the flag
2449        #[cfg(debug_assertions)]
2450        if log_enabled!(Debug) && self.graph.debug {
2451            debug!("resolving the following graph:\n\n{:#?}\n\n", self.graph);
2452        }
2453
2454        debug_assert!(
2455            schedule.passes.windows(2).all(|w| w[0] <= w[1]),
2456            "Unsorted schedule"
2457        );
2458
2459        // Optimize the schedule; leasing the required stuff it needs
2460        Self::reorder_scheduled_passes(schedule, end_pass_idx);
2461        self.merge_scheduled_passes(&mut schedule.passes);
2462        self.lease_scheduled_resources(pool, &schedule.passes)?;
2463
2464        for pass_idx in schedule.passes.iter().copied() {
2465            let pass = &mut self.graph.passes[pass_idx];
2466
2467            profiling::scope!("Pass", &pass.name);
2468
2469            let physical_pass = &mut self.physical_passes[pass_idx];
2470            let is_graphic = physical_pass.render_pass.is_some();
2471
2472            trace!("recording pass [{}: {}]", pass_idx, pass.name);
2473
2474            if !physical_pass.exec_descriptor_sets.is_empty() {
2475                Self::write_descriptor_sets(cmd_buf, &self.graph.bindings, pass, physical_pass)?;
2476            }
2477
2478            let render_area = if is_graphic {
2479                Self::record_image_layout_transitions(cmd_buf, &mut self.graph.bindings, pass);
2480
2481                let render_area = Self::render_area(&self.graph.bindings, pass);
2482
2483                Self::begin_render_pass(
2484                    cmd_buf,
2485                    &self.graph.bindings,
2486                    pass,
2487                    physical_pass,
2488                    render_area,
2489                )?;
2490
2491                Some(render_area)
2492            } else {
2493                None
2494            };
2495
2496            for exec_idx in 0..pass.execs.len() {
2497                let render_area = is_graphic.then(|| {
2498                    pass.execs[exec_idx]
2499                        .render_area
2500                        .unwrap_or(render_area.unwrap())
2501                });
2502
2503                let exec = &mut pass.execs[exec_idx];
2504
2505                if is_graphic && exec_idx > 0 {
2506                    Self::next_subpass(cmd_buf);
2507                }
2508
2509                if let Some(pipeline) = exec.pipeline.as_mut() {
2510                    Self::bind_pipeline(
2511                        cmd_buf,
2512                        physical_pass,
2513                        exec_idx,
2514                        pipeline,
2515                        exec.depth_stencil,
2516                    )?;
2517
2518                    if is_graphic {
2519                        let render_area = render_area.unwrap();
2520
2521                        // In this case we set the viewport and scissor for the user
2522                        Self::set_viewport(
2523                            cmd_buf,
2524                            render_area.x as _,
2525                            render_area.y as _,
2526                            render_area.width as _,
2527                            render_area.height as _,
2528                            exec.depth_stencil
2529                                .map(|depth_stencil| {
2530                                    let min = depth_stencil.min.0;
2531                                    let max = depth_stencil.max.0;
2532                                    min..max
2533                                })
2534                                .unwrap_or(0.0..1.0),
2535                        );
2536                        Self::set_scissor(
2537                            cmd_buf,
2538                            render_area.x,
2539                            render_area.y,
2540                            render_area.width,
2541                            render_area.height,
2542                        );
2543                    }
2544
2545                    Self::bind_descriptor_sets(cmd_buf, pipeline, physical_pass, exec_idx);
2546                }
2547
2548                if !is_graphic {
2549                    Self::record_execution_barriers(
2550                        cmd_buf,
2551                        &mut self.graph.bindings,
2552                        exec.accesses.iter(),
2553                    );
2554                }
2555
2556                trace!("    > exec[{exec_idx}]");
2557
2558                {
2559                    profiling::scope!("Execute callback");
2560
2561                    let exec_func = exec.func.take().unwrap().0;
2562                    exec_func(
2563                        &cmd_buf.device,
2564                        **cmd_buf,
2565                        Bindings::new(&self.graph.bindings, exec),
2566                    );
2567                }
2568            }
2569
2570            if is_graphic {
2571                self.end_render_pass(cmd_buf);
2572            }
2573        }
2574
2575        thread_local! {
2576            static PASSES: RefCell<Vec<Pass>> = Default::default();
2577        }
2578
2579        PASSES.with_borrow_mut(|passes| {
2580            debug_assert!(passes.is_empty());
2581
2582            // We have to keep the bindings and pipelines alive until the gpu is done
2583            schedule.passes.sort_unstable();
2584            while let Some(schedule_idx) = schedule.passes.pop() {
2585                debug_assert!(!self.graph.passes.is_empty());
2586
2587                while let Some(pass) = self.graph.passes.pop() {
2588                    let pass_idx = self.graph.passes.len();
2589
2590                    if pass_idx == schedule_idx {
2591                        // This was a scheduled pass - store it!
2592                        CommandBuffer::push_fenced_drop(
2593                            cmd_buf,
2594                            (pass, self.physical_passes.pop().unwrap()),
2595                        );
2596                        break;
2597                    } else {
2598                        debug_assert!(pass_idx > schedule_idx);
2599
2600                        passes.push(pass);
2601                    }
2602                }
2603            }
2604
2605            debug_assert!(self.physical_passes.is_empty());
2606
2607            // Put the other passes back for future resolves
2608            self.graph.passes.extend(passes.drain(..).rev());
2609        });
2610
2611        log::trace!("Recorded passes");
2612
2613        Ok(())
2614    }
2615
2616    /// Records any pending render graph passes that have not been previously scheduled.
2617    #[profiling::function]
2618    pub fn record_unscheduled_passes<P>(
2619        &mut self,
2620        pool: &mut P,
2621        cmd_buf: &mut CommandBuffer,
2622    ) -> Result<(), DriverError>
2623    where
2624        P: Pool<DescriptorPoolInfo, DescriptorPool> + Pool<RenderPassInfo, RenderPass>,
2625    {
2626        if self.graph.passes.is_empty() {
2627            return Ok(());
2628        }
2629
2630        thread_local! {
2631            static SCHEDULE: RefCell<Schedule> = Default::default();
2632        }
2633
2634        SCHEDULE.with_borrow_mut(|schedule| {
2635            schedule
2636                .access_cache
2637                .update(&self.graph, self.graph.passes.len());
2638            schedule.passes.clear();
2639            schedule.passes.extend(0..self.graph.passes.len());
2640
2641            self.record_scheduled_passes(pool, cmd_buf, schedule, self.graph.passes.len())
2642        })
2643    }
2644
2645    #[profiling::function]
2646    fn render_area(bindings: &[Binding], pass: &Pass) -> Area {
2647        // set_render_area was not specified so we're going to guess using the minimum common
2648        // attachment extents
2649        let first_exec = pass.execs.first().unwrap();
2650
2651        // We must be able to find the render area because render passes require at least one
2652        // image to be attached
2653        let (mut width, mut height) = (u32::MAX, u32::MAX);
2654        for (attachment_width, attachment_height) in first_exec
2655            .color_clears
2656            .values()
2657            .copied()
2658            .map(|(attachment, _)| attachment)
2659            .chain(first_exec.color_loads.values().copied())
2660            .chain(first_exec.color_stores.values().copied())
2661            .chain(
2662                first_exec
2663                    .depth_stencil_clear
2664                    .map(|(attachment, _)| attachment),
2665            )
2666            .chain(first_exec.depth_stencil_load)
2667            .chain(first_exec.depth_stencil_store)
2668            .map(|attachment| {
2669                let info = bindings[attachment.target].as_driver_image().unwrap().info;
2670
2671                (
2672                    info.width >> attachment.base_mip_level,
2673                    info.height >> attachment.base_mip_level,
2674                )
2675            })
2676        {
2677            width = width.min(attachment_width);
2678            height = height.min(attachment_height);
2679        }
2680
2681        Area {
2682            height,
2683            width,
2684            x: 0,
2685            y: 0,
2686        }
2687    }
2688
2689    #[profiling::function]
2690    fn reorder_scheduled_passes(schedule: &mut Schedule, end_pass_idx: usize) {
2691        // It must be a party
2692        if schedule.passes.len() < 3 {
2693            return;
2694        }
2695
2696        let mut scheduled = 0;
2697
2698        thread_local! {
2699            static UNSCHEDULED: RefCell<Vec<bool>> = Default::default();
2700        }
2701
2702        UNSCHEDULED.with_borrow_mut(|unscheduled| {
2703            unscheduled.truncate(end_pass_idx);
2704            unscheduled.fill(true);
2705            unscheduled.resize(end_pass_idx, true);
2706
2707            // Re-order passes by maximizing the distance between dependent nodes
2708            while scheduled < schedule.passes.len() {
2709                let mut best_idx = scheduled;
2710                let pass_idx = schedule.passes[best_idx];
2711                let mut best_overlap_factor = schedule
2712                    .access_cache
2713                    .interdependent_passes(pass_idx, end_pass_idx)
2714                    .count();
2715
2716                for (idx, pass_idx) in schedule.passes[best_idx + 1..schedule.passes.len()]
2717                    .iter()
2718                    .enumerate()
2719                {
2720                    let mut overlap_factor = 0;
2721
2722                    for other_pass_idx in schedule
2723                        .access_cache
2724                        .interdependent_passes(*pass_idx, end_pass_idx)
2725                    {
2726                        if unscheduled[other_pass_idx] {
2727                            // This pass can't be the candidate because it depends on unfinished work
2728                            break;
2729                        }
2730
2731                        overlap_factor += 1;
2732                    }
2733
2734                    if overlap_factor > best_overlap_factor {
2735                        best_idx += idx + 1;
2736                        best_overlap_factor = overlap_factor;
2737                    }
2738                }
2739
2740                unscheduled[schedule.passes[best_idx]] = false;
2741                schedule.passes.swap(scheduled, best_idx);
2742                scheduled += 1;
2743            }
2744        });
2745    }
2746
2747    /// Returns a vec of pass indexes that are required to be executed, in order, for the given
2748    /// node.
2749    #[profiling::function]
2750    fn schedule_node_passes(&self, node_idx: usize, end_pass_idx: usize, schedule: &mut Schedule) {
2751        type UnscheduledUnresolvedUnchecked = (Vec<bool>, Vec<bool>, VecDeque<(usize, usize)>);
2752
2753        thread_local! {
2754            static UNSCHEDULED_UNRESOLVED_UNCHECKED: RefCell<UnscheduledUnresolvedUnchecked> = Default::default();
2755        }
2756
2757        UNSCHEDULED_UNRESOLVED_UNCHECKED.with_borrow_mut(|(unscheduled, unresolved, unchecked)| {
2758            unscheduled.truncate(end_pass_idx);
2759            unscheduled.fill(true);
2760            unscheduled.resize(end_pass_idx, true);
2761
2762            unresolved.truncate(self.graph.bindings.len());
2763            unresolved.fill(true);
2764            unresolved.resize(self.graph.bindings.len(), true);
2765
2766            debug_assert!(unchecked.is_empty());
2767
2768            trace!("scheduling node {node_idx}");
2769
2770            unresolved[node_idx] = false;
2771
2772            // Schedule the first set of passes for the node we're trying to resolve
2773            for pass_idx in schedule
2774                .access_cache
2775                .dependent_passes(node_idx, end_pass_idx)
2776            {
2777                trace!(
2778                    "  pass [{pass_idx}: {}] is dependent",
2779                    self.graph.passes[pass_idx].name
2780                );
2781
2782                debug_assert!(unscheduled[pass_idx]);
2783
2784                unscheduled[pass_idx] = false;
2785                schedule.passes.push(pass_idx);
2786
2787                for node_idx in schedule.access_cache.dependent_nodes(pass_idx) {
2788                    trace!("    node {node_idx} is dependent");
2789
2790                    let unresolved = &mut unresolved[node_idx];
2791                    if *unresolved {
2792                        *unresolved = false;
2793                        unchecked.push_back((node_idx, pass_idx));
2794                    }
2795                }
2796            }
2797
2798            trace!("secondary passes below");
2799
2800            // Now schedule all nodes that are required, going through the tree to find them
2801            while let Some((node_idx, pass_idx)) = unchecked.pop_front() {
2802                trace!("  node {node_idx} is dependent");
2803
2804                for pass_idx in schedule
2805                    .access_cache
2806                    .dependent_passes(node_idx, pass_idx + 1)
2807                {
2808                    let unscheduled = &mut unscheduled[pass_idx];
2809                    if *unscheduled {
2810                        *unscheduled = false;
2811                        schedule.passes.push(pass_idx);
2812
2813                        trace!(
2814                            "  pass [{pass_idx}: {}] is dependent",
2815                            self.graph.passes[pass_idx].name
2816                        );
2817
2818                        for node_idx in schedule.access_cache.dependent_nodes(pass_idx) {
2819                            trace!("    node {node_idx} is dependent");
2820
2821                            let unresolved = &mut unresolved[node_idx];
2822                            if *unresolved {
2823                                *unresolved = false;
2824                                unchecked.push_back((node_idx, pass_idx));
2825                            }
2826                        }
2827                    }
2828                }
2829            }
2830
2831            schedule.passes.sort_unstable();
2832
2833            if log_enabled!(Debug) {
2834                if !schedule.passes.is_empty() {
2835                    // These are the indexes of the passes this thread is about to resolve
2836                    debug!(
2837                        "schedule: {}",
2838                        schedule
2839                            .passes
2840                            .iter()
2841                            .copied()
2842                            .map(|idx| format!("[{}: {}]", idx, self.graph.passes[idx].name))
2843                            .collect::<Vec<_>>()
2844                            .join(", ")
2845                    );
2846                }
2847
2848                if log_enabled!(Trace) {
2849                    let unscheduled = (0..end_pass_idx)
2850                        .filter(|&pass_idx| unscheduled[pass_idx])
2851                        .collect::<Box<_>>();
2852
2853                    if !unscheduled.is_empty() {
2854                        // These passes are within the range of passes we thought we had to do
2855                        // right now, but it turns out that nothing in "schedule" relies on them
2856                        trace!(
2857                            "delaying: {}",
2858                            unscheduled
2859                                .iter()
2860                                .copied()
2861                                .map(|idx| format!("[{}: {}]", idx, self.graph.passes[idx].name))
2862                                .collect::<Vec<_>>()
2863                                .join(", ")
2864                        );
2865                    }
2866
2867                    if end_pass_idx < self.graph.passes.len() {
2868                        // These passes existing on the graph but are not being considered right
2869                        // now because we've been told to stop work at the "end_pass_idx" point
2870                        trace!(
2871                            "ignoring: {}",
2872                            self.graph.passes[end_pass_idx..]
2873                                .iter()
2874                                .enumerate()
2875                                .map(|(idx, pass)| format!(
2876                                    "[{}: {}]",
2877                                    idx + end_pass_idx,
2878                                    pass.name
2879                                ))
2880                                .collect::<Vec<_>>()
2881                                .join(", ")
2882                        );
2883                    }
2884                }
2885            }
2886        });
2887    }
2888
2889    fn set_scissor(cmd_buf: &CommandBuffer, x: i32, y: i32, width: u32, height: u32) {
2890        use std::slice::from_ref;
2891
2892        unsafe {
2893            cmd_buf.device.cmd_set_scissor(
2894                **cmd_buf,
2895                0,
2896                from_ref(&vk::Rect2D {
2897                    extent: vk::Extent2D { width, height },
2898                    offset: vk::Offset2D { x, y },
2899                }),
2900            );
2901        }
2902    }
2903
2904    fn set_viewport(
2905        cmd_buf: &CommandBuffer,
2906        x: f32,
2907        y: f32,
2908        width: f32,
2909        height: f32,
2910        depth: Range<f32>,
2911    ) {
2912        use std::slice::from_ref;
2913
2914        unsafe {
2915            cmd_buf.device.cmd_set_viewport(
2916                **cmd_buf,
2917                0,
2918                from_ref(&vk::Viewport {
2919                    x,
2920                    y,
2921                    width,
2922                    height,
2923                    min_depth: depth.start,
2924                    max_depth: depth.end,
2925                }),
2926            );
2927        }
2928    }
2929
2930    /// Submits the remaining commands stored in this instance.
2931    #[profiling::function]
2932    pub fn submit<P>(
2933        mut self,
2934        pool: &mut P,
2935        queue_family_index: usize,
2936        queue_index: usize,
2937    ) -> Result<Lease<CommandBuffer>, DriverError>
2938    where
2939        P: Pool<CommandBufferInfo, CommandBuffer>
2940            + Pool<DescriptorPoolInfo, DescriptorPool>
2941            + Pool<RenderPassInfo, RenderPass>,
2942    {
2943        use std::slice::from_ref;
2944
2945        trace!("submit");
2946
2947        let mut cmd_buf = pool.lease(CommandBufferInfo::new(queue_family_index as _))?;
2948
2949        debug_assert!(
2950            queue_family_index < cmd_buf.device.physical_device.queue_families.len(),
2951            "Queue family index must be within the range of the available queues created by the device."
2952        );
2953        debug_assert!(
2954            queue_index
2955                < cmd_buf.device.physical_device.queue_families[queue_family_index].queue_count
2956                    as usize,
2957            "Queue index must be within the range of the available queues created by the device."
2958        );
2959
2960        CommandBuffer::wait_until_executed(&mut cmd_buf)?;
2961
2962        unsafe {
2963            cmd_buf
2964                .device
2965                .begin_command_buffer(
2966                    **cmd_buf,
2967                    &vk::CommandBufferBeginInfo::default()
2968                        .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
2969                )
2970                .map_err(|_| DriverError::OutOfMemory)?;
2971        }
2972
2973        self.record_unscheduled_passes(pool, &mut cmd_buf)?;
2974
2975        unsafe {
2976            cmd_buf
2977                .device
2978                .end_command_buffer(**cmd_buf)
2979                .map_err(|_| DriverError::OutOfMemory)?;
2980            cmd_buf
2981                .device
2982                .reset_fences(from_ref(&cmd_buf.fence))
2983                .map_err(|_| DriverError::OutOfMemory)?;
2984            cmd_buf
2985                .device
2986                .queue_submit(
2987                    cmd_buf.device.queues[queue_family_index][queue_index],
2988                    from_ref(&vk::SubmitInfo::default().command_buffers(from_ref(&cmd_buf))),
2989                    cmd_buf.fence,
2990                )
2991                .map_err(|_| DriverError::OutOfMemory)?;
2992        }
2993
2994        cmd_buf.waiting = true;
2995
2996        // This graph contains references to buffers, images, and other resources which must be kept
2997        // alive until this graph execution completes on the GPU. Once those references are dropped
2998        // they will return to the pool for other things to use. The drop will happen the next time
2999        // someone tries to lease a command buffer and we notice this one has returned and the fence
3000        // has been signalled.
3001        CommandBuffer::push_fenced_drop(&mut cmd_buf, self);
3002
3003        Ok(cmd_buf)
3004    }
3005
3006    pub(crate) fn swapchain_image(&mut self, node: SwapchainImageNode) -> &SwapchainImage {
3007        let Some(swapchain_image) = self.graph.bindings[node.idx].as_swapchain_image() else {
3008            panic!("invalid swapchain image node");
3009        };
3010
3011        swapchain_image
3012    }
3013
3014    #[profiling::function]
3015    fn write_descriptor_sets(
3016        cmd_buf: &CommandBuffer,
3017        bindings: &[Binding],
3018        pass: &Pass,
3019        physical_pass: &PhysicalPass,
3020    ) -> Result<(), DriverError> {
3021        struct IndexWrite<'a> {
3022            idx: usize,
3023            write: vk::WriteDescriptorSet<'a>,
3024        }
3025
3026        #[derive(Default)]
3027        struct Tls<'a> {
3028            accel_struct_infos: Vec<vk::WriteDescriptorSetAccelerationStructureKHR<'a>>,
3029            accel_struct_writes: Vec<IndexWrite<'a>>,
3030            buffer_infos: Vec<vk::DescriptorBufferInfo>,
3031            buffer_writes: Vec<IndexWrite<'a>>,
3032            descriptors: Vec<vk::WriteDescriptorSet<'a>>,
3033            image_infos: Vec<vk::DescriptorImageInfo>,
3034            image_writes: Vec<IndexWrite<'a>>,
3035        }
3036
3037        let mut tls = Tls::default();
3038
3039        for (exec_idx, exec, pipeline) in pass
3040            .execs
3041            .iter()
3042            .enumerate()
3043            .filter_map(|(exec_idx, exec)| {
3044                exec.pipeline
3045                    .as_ref()
3046                    .map(|pipeline| (exec_idx, exec, pipeline))
3047            })
3048            .filter(|(.., pipeline)| !pipeline.descriptor_info().layouts.is_empty())
3049        {
3050            let descriptor_sets = &physical_pass.exec_descriptor_sets[&exec_idx];
3051
3052            // Write the manually bound things (access, read, and write functions)
3053            for (descriptor, (node_idx, view_info)) in exec.bindings.iter() {
3054                let (descriptor_set_idx, dst_binding, binding_offset) = descriptor.into_tuple();
3055                let (descriptor_info, _) = pipeline
3056                        .descriptor_bindings()
3057                        .get(&Descriptor { set: descriptor_set_idx, binding: dst_binding })
3058                        .unwrap_or_else(|| panic!("descriptor {descriptor_set_idx}.{dst_binding}[{binding_offset}] specified in recorded execution of pass \"{}\" was not discovered through shader reflection", &pass.name));
3059                let descriptor_type = descriptor_info.descriptor_type();
3060                let bound_node = &bindings[*node_idx];
3061                if let Some(image) = bound_node.as_driver_image() {
3062                    let view_info = view_info.as_ref().unwrap();
3063                    let mut image_view_info = *view_info.as_image().unwrap();
3064
3065                    // Handle default views which did not specify a particaular aspect
3066                    if image_view_info.aspect_mask.is_empty() {
3067                        image_view_info.aspect_mask = format_aspect_mask(image.info.fmt);
3068                    }
3069
3070                    let image_view = Image::view(image, image_view_info)?;
3071                    let image_layout = match descriptor_type {
3072                        vk::DescriptorType::COMBINED_IMAGE_SAMPLER
3073                        | vk::DescriptorType::SAMPLED_IMAGE => {
3074                            if image_view_info.aspect_mask.contains(
3075                                vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL,
3076                            ) {
3077                                vk::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL
3078                            } else if image_view_info
3079                                .aspect_mask
3080                                .contains(vk::ImageAspectFlags::DEPTH)
3081                            {
3082                                vk::ImageLayout::DEPTH_READ_ONLY_OPTIMAL
3083                            } else if image_view_info
3084                                .aspect_mask
3085                                .contains(vk::ImageAspectFlags::STENCIL)
3086                            {
3087                                vk::ImageLayout::STENCIL_READ_ONLY_OPTIMAL
3088                            } else {
3089                                vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL
3090                            }
3091                        }
3092                        vk::DescriptorType::STORAGE_IMAGE => vk::ImageLayout::GENERAL,
3093                        _ => unimplemented!("{descriptor_type:?}"),
3094                    };
3095
3096                    if binding_offset == 0 {
3097                        tls.image_writes.push(IndexWrite {
3098                            idx: tls.image_infos.len(),
3099                            write: vk::WriteDescriptorSet {
3100                                dst_set: *descriptor_sets[descriptor_set_idx as usize],
3101                                dst_binding,
3102                                descriptor_type,
3103                                descriptor_count: 1,
3104                                ..Default::default()
3105                            },
3106                        });
3107                    } else {
3108                        tls.image_writes.last_mut().unwrap().write.descriptor_count += 1;
3109                    }
3110
3111                    tls.image_infos.push(
3112                        vk::DescriptorImageInfo::default()
3113                            .image_layout(image_layout)
3114                            .image_view(image_view),
3115                    );
3116                } else if let Some(buffer) = bound_node.as_driver_buffer() {
3117                    let view_info = view_info.as_ref().unwrap();
3118                    let buffer_view_info = view_info.as_buffer().unwrap();
3119
3120                    if binding_offset == 0 {
3121                        tls.buffer_writes.push(IndexWrite {
3122                            idx: tls.buffer_infos.len(),
3123                            write: vk::WriteDescriptorSet {
3124                                dst_set: *descriptor_sets[descriptor_set_idx as usize],
3125                                dst_binding,
3126                                descriptor_type,
3127                                descriptor_count: 1,
3128                                ..Default::default()
3129                            },
3130                        });
3131                    } else {
3132                        tls.buffer_writes.last_mut().unwrap().write.descriptor_count += 1;
3133                    }
3134
3135                    tls.buffer_infos.push(
3136                        vk::DescriptorBufferInfo::default()
3137                            .buffer(**buffer)
3138                            .offset(buffer_view_info.start)
3139                            .range(buffer_view_info.end - buffer_view_info.start),
3140                    );
3141                } else if let Some(accel_struct) = bound_node.as_driver_acceleration_structure() {
3142                    if binding_offset == 0 {
3143                        tls.accel_struct_writes.push(IndexWrite {
3144                            idx: tls.accel_struct_infos.len(),
3145                            write: vk::WriteDescriptorSet::default()
3146                                .dst_set(*descriptor_sets[descriptor_set_idx as usize])
3147                                .dst_binding(dst_binding)
3148                                .descriptor_type(descriptor_type)
3149                                .descriptor_count(1),
3150                        });
3151                    } else {
3152                        tls.accel_struct_writes
3153                            .last_mut()
3154                            .unwrap()
3155                            .write
3156                            .descriptor_count += 1;
3157                    }
3158
3159                    tls.accel_struct_infos.push(
3160                        vk::WriteDescriptorSetAccelerationStructureKHR::default()
3161                            .acceleration_structures(std::slice::from_ref(accel_struct)),
3162                    );
3163                } else {
3164                    unimplemented!();
3165                }
3166            }
3167
3168            if let ExecutionPipeline::Graphic(pipeline) = pipeline {
3169                // Write graphic render pass input attachments (they're automatic)
3170                if exec_idx > 0 {
3171                    for (
3172                        &Descriptor {
3173                            set: descriptor_set_idx,
3174                            binding: dst_binding,
3175                        },
3176                        (descriptor_info, _),
3177                    ) in &pipeline.descriptor_bindings
3178                    {
3179                        if let DescriptorInfo::InputAttachment(_, attachment_idx) = *descriptor_info
3180                        {
3181                            let is_random_access = exec.color_stores.contains_key(&attachment_idx)
3182                                || exec.color_resolves.contains_key(&attachment_idx);
3183                            let (attachment, write_exec) = pass.execs[0..exec_idx]
3184                                .iter()
3185                                .rev()
3186                                .find_map(|exec| {
3187                                    exec.color_stores
3188                                        .get(&attachment_idx)
3189                                        .copied()
3190                                        .map(|attachment| (attachment, exec))
3191                                        .or_else(|| {
3192                                            exec.color_resolves.get(&attachment_idx).map(
3193                                                |(resolved_attachment, _)| {
3194                                                    (*resolved_attachment, exec)
3195                                                },
3196                                            )
3197                                        })
3198                                })
3199                                .expect("input attachment not written");
3200                            let late = &write_exec.accesses[&attachment.target].last().unwrap();
3201                            let image_range = late.subresource.as_image().unwrap();
3202                            let image_binding = &bindings[attachment.target];
3203                            let image = image_binding.as_driver_image().unwrap();
3204                            let image_view_info = ImageViewInfo {
3205                                array_layer_count: image_range.layer_count,
3206                                aspect_mask: attachment.aspect_mask,
3207                                base_array_layer: image_range.base_array_layer,
3208                                base_mip_level: image_range.base_mip_level,
3209                                fmt: attachment.format,
3210                                mip_level_count: image_range.level_count,
3211                                ty: image.info.ty,
3212                            };
3213                            let image_view = Image::view(image, image_view_info)?;
3214
3215                            tls.image_writes.push(IndexWrite {
3216                                idx: tls.image_infos.len(),
3217                                write: vk::WriteDescriptorSet {
3218                                    dst_set: *descriptor_sets[descriptor_set_idx as usize],
3219                                    dst_binding,
3220                                    descriptor_type: vk::DescriptorType::INPUT_ATTACHMENT,
3221                                    descriptor_count: 1,
3222                                    ..Default::default()
3223                                },
3224                            });
3225
3226                            tls.image_infos.push(vk::DescriptorImageInfo {
3227                                image_layout: Self::attachment_layout(
3228                                    attachment.aspect_mask,
3229                                    is_random_access,
3230                                    true,
3231                                ),
3232                                image_view,
3233                                sampler: vk::Sampler::null(),
3234                            });
3235                        }
3236                    }
3237                }
3238            }
3239        }
3240
3241        // NOTE: We assign the below pointers after the above insertions so they remain stable!
3242
3243        tls.descriptors
3244            .extend(tls.accel_struct_writes.drain(..).map(
3245                |IndexWrite { idx, mut write }| unsafe {
3246                    write.p_next = tls.accel_struct_infos.as_ptr().add(idx) as *const _;
3247                    write
3248                },
3249            ));
3250        tls.descriptors.extend(tls.buffer_writes.drain(..).map(
3251            |IndexWrite { idx, mut write }| unsafe {
3252                write.p_buffer_info = tls.buffer_infos.as_ptr().add(idx);
3253                write
3254            },
3255        ));
3256        tls.descriptors.extend(tls.image_writes.drain(..).map(
3257            |IndexWrite { idx, mut write }| unsafe {
3258                write.p_image_info = tls.image_infos.as_ptr().add(idx);
3259                write
3260            },
3261        ));
3262
3263        if !tls.descriptors.is_empty() {
3264            trace!(
3265                "  writing {} descriptors ({} buffers, {} images)",
3266                tls.descriptors.len(),
3267                tls.buffer_infos.len(),
3268                tls.image_infos.len()
3269            );
3270
3271            unsafe {
3272                cmd_buf
3273                    .device
3274                    .update_descriptor_sets(tls.descriptors.as_slice(), &[]);
3275            }
3276        }
3277
3278        Ok(())
3279    }
3280}
3281
3282#[derive(Default)]
3283struct Schedule {
3284    access_cache: AccessCache,
3285    passes: Vec<usize>,
3286}