Skip to main content

vk_graph/
submission.rs

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