rafx_framework/graph/
graph_plan.rs

1use super::*;
2use super::{RenderGraphExternalImageId, RenderGraphImageSpecification};
3use crate::graph::graph_image::{PhysicalImageId, RenderGraphImageUser, VirtualImageId};
4use crate::graph::graph_node::RenderGraphNodeId;
5use crate::graph::{RenderGraphBuilder, RenderGraphImageConstraint, RenderGraphImageUsageId};
6use crate::render_features::RenderPhaseIndex;
7use crate::{BufferResource, GraphicsPipelineRenderTargetMeta};
8use crate::{ImageViewResource, ResourceArc};
9use fnv::{FnvHashMap, FnvHashSet};
10use rafx_api::{RafxFormat, RafxLoadOp, RafxResourceState, RafxSampleCount, RafxStoreOp};
11
12// Recursively called to topologically sort the nodes to determine execution order. See
13// determine_node_order which kicks this off.
14// https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search
15fn visit_node(
16    graph: &RenderGraphBuilder,
17    node_id: RenderGraphNodeId,
18    visited: &mut Vec<bool>,
19    visiting: &mut Vec<bool>,
20    visiting_stack: &mut Vec<RenderGraphNodeId>,
21    ordered_list: &mut Vec<RenderGraphNodeId>,
22) {
23    // This node is already visited and inserted into ordered_list
24    if visited[node_id.0] {
25        return;
26    }
27
28    // This node is already being visited higher up in the stack. This indicates a cycle in the
29    // graph
30    if visiting[node_id.0] {
31        log::warn!("Found cycle in graph");
32        log::warn!("{:?}", graph.node(node_id));
33        for v in visiting_stack.iter().rev() {
34            log::warn!("{:?}", graph.node(*v));
35        }
36        panic!("Graph has a cycle");
37    }
38
39    // When we enter the node, mark the node as being in-progress of being visited to help
40    // detect cycles in the graph
41    visiting[node_id.0] = true;
42    visiting_stack.push(node_id);
43
44    //
45    // Visit children
46    //
47    //log::trace!("  Begin visit {:?}", node_id);
48    let node = graph.node(node_id);
49
50    //
51    // Visit all the nodes we aren't delaying
52    //
53    for read in &node.image_reads {
54        let upstream_node = graph.image_version_info(read.image).creator_node;
55        visit_node(
56            graph,
57            upstream_node,
58            visited,
59            visiting,
60            visiting_stack,
61            ordered_list,
62        );
63    }
64
65    for modify in &node.image_modifies {
66        let upstream_node = graph.image_version_info(modify.input).creator_node;
67        visit_node(
68            graph,
69            upstream_node,
70            visited,
71            visiting,
72            visiting_stack,
73            ordered_list,
74        );
75    }
76
77    for read in &node.buffer_reads {
78        let upstream_node = graph.buffer_version_info(read.buffer).creator_node;
79        visit_node(
80            graph,
81            upstream_node,
82            visited,
83            visiting,
84            visiting_stack,
85            ordered_list,
86        );
87    }
88
89    for modify in &node.buffer_modifies {
90        let upstream_node = graph.buffer_version_info(modify.input).creator_node;
91        visit_node(
92            graph,
93            upstream_node,
94            visited,
95            visiting,
96            visiting_stack,
97            ordered_list,
98        );
99    }
100
101    for &explicit_dependency in &node.explicit_dependencies {
102        visit_node(
103            graph,
104            explicit_dependency,
105            visited,
106            visiting,
107            visiting_stack,
108            ordered_list,
109        );
110    }
111
112    // All our pre-requisites were visited, so it's now safe to push this node onto the
113    // orderered list
114    ordered_list.push(node_id);
115    visited[node_id.0] = true;
116
117    // We are no longer visiting this node
118    //log::trace!("  End visit {:?}", node_id);
119    visiting_stack.pop();
120    visiting[node_id.0] = false;
121}
122
123//
124// The purpose of this function is to determine the order that nodes should execute in. We do this
125// by following the graph from the outputs backwards.
126//
127#[profiling::function]
128fn determine_node_order(graph: &RenderGraphBuilder) -> Vec<RenderGraphNodeId> {
129    // As we depth-first traverse nodes, mark them as visiting and push them onto this stack.
130    // We will use this to detect and print out cycles
131    let mut visiting = vec![false; graph.nodes.len()];
132    let mut visiting_stack = Vec::default();
133
134    // The order of nodes, upstream to downstream. As we depth-first traverse nodes, push nodes
135    // with no unvisited dependencies onto this list and mark them as visited
136    let mut visited = vec![false; graph.nodes.len()];
137    let mut ordered_list = Vec::default();
138
139    // Iterate all the images we need to output. This will visit all the nodes we need to execute,
140    // potentially leaving out nodes we can cull.
141    for external_image in &graph.external_images {
142        if let Some(output_usage) = external_image.output_usage {
143            // Find the node that creates the output image
144            let output_node = graph.image_version_info(output_usage).creator_node;
145            log::trace!(
146                "Traversing dependencies of output image created by node {:?} {:?}",
147                output_node,
148                graph.node(output_node).name()
149            );
150
151            visit_node(
152                graph,
153                output_node,
154                &mut visited,
155                &mut visiting,
156                &mut visiting_stack,
157                &mut ordered_list,
158            );
159        }
160    }
161
162    // Iterate all the buffers we need to output. This will visit all the nodes we need to execute,
163    // potentially leaving out nodes we can cull.
164    for external_buffer in &graph.external_buffers {
165        if let Some(output_usage) = external_buffer.output_usage {
166            // Find the node that creates the output buffer
167            let output_node = graph.buffer_version_info(output_usage).creator_node;
168            log::trace!(
169                "Traversing dependencies of output buffer created by node {:?} {:?}",
170                output_node,
171                graph.node(output_node).name()
172            );
173
174            visit_node(
175                graph,
176                output_node,
177                &mut visited,
178                &mut visiting,
179                &mut visiting_stack,
180                &mut ordered_list,
181            );
182        }
183    }
184
185    for node in &graph.nodes {
186        if !node.can_be_culled {
187            visit_node(
188                graph,
189                node.id(),
190                &mut visited,
191                &mut visiting,
192                &mut visiting_stack,
193                &mut ordered_list,
194            );
195        }
196    }
197
198    ordered_list
199}
200
201/// The specification for the image by image usage
202pub struct DetermineConstraintsResult {
203    images: FnvHashMap<RenderGraphImageUsageId, RenderGraphImageSpecification>,
204    buffers: FnvHashMap<RenderGraphBufferUsageId, RenderGraphBufferSpecification>,
205}
206
207impl DetermineConstraintsResult {
208    pub fn image_specification(
209        &self,
210        image: RenderGraphImageUsageId,
211    ) -> Option<&RenderGraphImageSpecification> {
212        self.images.get(&image)
213    }
214
215    pub fn buffer_specification(
216        &self,
217        buffer: RenderGraphBufferUsageId,
218    ) -> Option<&RenderGraphBufferSpecification> {
219        self.buffers.get(&buffer)
220    }
221}
222
223//
224// This function determines the specifications of all images. This is done by looking at the
225// constraints on the image at every point it is used. This information and how the image is used
226// will determine the image specification. (The specification is all the information needed to
227// create the image. Conflicting constraints or incomplete constraints will result in an error.
228//
229// The general algorithm here is to start from the beginning of the graph, walk forward to the end,
230// propagating the constraints. Then walk backwards from the end to the beginning.
231//
232#[profiling::function]
233fn determine_constraints(
234    graph: &RenderGraphBuilder,
235    node_execution_order: &[RenderGraphNodeId],
236    swapchain_surface_info: &SwapchainSurfaceInfo,
237) -> DetermineConstraintsResult {
238    let mut image_version_states: FnvHashMap<RenderGraphImageUsageId, RenderGraphImageConstraint> =
239        Default::default();
240
241    let mut buffer_version_states: FnvHashMap<
242        RenderGraphBufferUsageId,
243        RenderGraphBufferConstraint,
244    > = Default::default();
245
246    log::trace!("Propagating constraints");
247
248    log::trace!("  Set up input images");
249
250    //
251    // Propagate input image state specifications into images. Inputs are fully specified and
252    // their constraints will never be overwritten
253    //
254    for external_image in &graph.external_images {
255        if let Some(input_usage) = external_image.input_usage {
256            log::trace!(
257                "    Image {:?} {:?}",
258                input_usage,
259                graph.image_resource(input_usage).name
260            );
261            debug_assert_eq!(graph.image_version_create_usage(input_usage), input_usage);
262            image_version_states
263                .entry(input_usage)
264                .or_default()
265                .set(&external_image.specification);
266
267            // Don't bother setting usage constraint for 0
268        }
269    }
270
271    log::trace!("  Set up input buffers");
272
273    //
274    // Propagate input buffer state specifications into buffers. Inputs are fully specified and
275    // their constraints will never be overwritten
276    //
277    for external_buffer in &graph.external_buffers {
278        if let Some(input_usage) = external_buffer.input_usage {
279            log::trace!(
280                "    Buffer {:?} {:?}",
281                input_usage,
282                graph.buffer_resource(input_usage).name
283            );
284            debug_assert_eq!(graph.buffer_version_create_usage(input_usage), input_usage);
285            buffer_version_states
286                .entry(input_usage)
287                .or_default()
288                .set(&external_buffer.specification);
289
290            // Don't bother setting usage constraint for 0
291        }
292    }
293
294    log::trace!("  Propagate constraints FORWARD");
295
296    //
297    // Iterate forward through nodes to determine what states images need to be in. We only need
298    // to handle operations that produce a new version of a resource. These operations do not
299    // need to fully specify image info, but whatever they do specify will be carried forward
300    // and not overwritten
301    //
302    for node_id in node_execution_order.iter() {
303        let node = graph.node(*node_id);
304        log::trace!("    node {:?} {:?}", node_id, node.name());
305
306        //
307        // Propagate constraints into images this node creates.
308        //
309        for image_create in &node.image_creates {
310            // An image cannot be created within the graph and imported externally at the same
311            // time. (The code here assumes no input and will not produce correct results if there
312            // is an input image)
313            //TODO: Input images are broken, we don't properly represent an image being created
314            // vs. receiving an input. We probably need to make creator in
315            // RenderGraphImageResourceVersionInfo Option or an enum with input/create options
316            //assert!(graph.image_version_info(image_create.image).input_image.is_none());
317
318            log::trace!(
319                "      Create image {:?} {:?}",
320                image_create.image,
321                graph.image_resource(image_create.image).name
322            );
323
324            let version_state = image_version_states
325                .entry(graph.image_version_create_usage(image_create.image))
326                .or_default();
327
328            if !version_state.try_merge(&image_create.constraint) {
329                // Should not happen as this should be our first visit to this image
330                panic!("Unexpected constraints on image being created");
331            }
332
333            log::trace!(
334                "        Forward propagate constraints {:?} {:?}",
335                image_create.image,
336                version_state
337            );
338
339            // Don't bother setting usage constraint for 0
340        }
341
342        //
343        // Propagate constraints into buffers this node creates.
344        //
345        for buffer_create in &node.buffer_creates {
346            // A buffer cannot be created within the graph and imported externally at the same
347            // time. (The code here assumes no input and will not produce correct results if there
348            // is an input buffer)
349            //TODO: Input buffers are broken, we don't properly represent a buffer being created
350            // vs. receiving an input. We probably need to make creator in
351            // RenderGraphImageResourceVersionInfo Option or an enum with input/create options
352            //assert!(graph.buffer_version_info(buffer_create.buffer).input_buffer.is_none());
353
354            log::trace!(
355                "      Create buffer {:?} {:?}",
356                buffer_create.buffer,
357                graph.buffer_resource(buffer_create.buffer).name
358            );
359
360            let version_state = buffer_version_states
361                .entry(graph.buffer_version_create_usage(buffer_create.buffer))
362                .or_default();
363
364            if !version_state.try_merge(&buffer_create.constraint) {
365                // Should not happen as this should be our first visit to this buffer
366                panic!("Unexpected constraints on buffer being created");
367            }
368
369            log::trace!(
370                "        Forward propagate constraints {:?} {:?}",
371                buffer_create.buffer,
372                version_state
373            );
374
375            // Don't bother setting usage constraint for 0
376        }
377
378        // We don't need to propagate anything forward on reads
379
380        fn propagate_image_constraints_forward(
381            graph: &RenderGraphBuilder,
382            image_version_states: &mut FnvHashMap<
383                RenderGraphImageUsageId,
384                RenderGraphImageConstraint,
385            >,
386            input: RenderGraphImageUsageId,
387            output: RenderGraphImageUsageId,
388            constraint: &RenderGraphImageConstraint,
389            operation_name: &str,
390        ) {
391            log::trace!(
392                "      {} image {:?} {:?} -> {:?} {:?}",
393                operation_name,
394                input,
395                graph.image_resource(input).name,
396                output,
397                graph.image_resource(output).name
398            );
399
400            //let image = graph.image_version_info(input);
401            //log::trace!("  Modify image {:?} {:?}", input, graph.image_resource(input).name);
402            let input_state = image_version_states
403                .entry(graph.image_version_create_usage(input))
404                .or_default();
405            let mut image_modify_constraint = constraint.clone();
406
407            // Merge the input image constraints with this node's constraints
408            image_modify_constraint.partial_merge(&input_state);
409
410            let output_state = image_version_states
411                .entry(graph.image_version_create_usage(output))
412                .or_default();
413
414            // Now propagate forward to the image version we write
415            output_state.partial_merge(&image_modify_constraint);
416
417            log::trace!("        Forward propagate constraints {:?}", output_state);
418        }
419
420        fn propagate_buffer_constraints_forward(
421            graph: &RenderGraphBuilder,
422            buffer_version_states: &mut FnvHashMap<
423                RenderGraphBufferUsageId,
424                RenderGraphBufferConstraint,
425            >,
426            input: RenderGraphBufferUsageId,
427            output: RenderGraphBufferUsageId,
428            constraint: &RenderGraphBufferConstraint,
429            operation_name: &str,
430        ) {
431            log::trace!(
432                "      {} buffer {:?} {:?} -> {:?} {:?}",
433                operation_name,
434                input,
435                graph.buffer_resource(input).name,
436                output,
437                graph.buffer_resource(output).name
438            );
439
440            //let buffer = graph.buffer_version_info(input);
441            //log::trace!("  Modify buffer {:?} {:?}", input, graph.buffer_resource(input).name);
442            let input_state = buffer_version_states
443                .entry(graph.buffer_version_create_usage(input))
444                .or_default();
445            let mut buffer_modify_constraint = constraint.clone();
446
447            // Merge the input buffer constraints with this node's constraints
448            buffer_modify_constraint.partial_merge(&input_state);
449
450            let output_state = buffer_version_states
451                .entry(graph.buffer_version_create_usage(output))
452                .or_default();
453
454            // Now propagate forward to the buffer version we write
455            output_state.partial_merge(&buffer_modify_constraint);
456
457            log::trace!("        Forward propagate constraints {:?}", output_state);
458        }
459
460        //
461        // Propagate constraints forward for images being modified.
462        //
463        for image_modify in &node.image_modifies {
464            propagate_image_constraints_forward(
465                graph,
466                &mut image_version_states,
467                image_modify.input,
468                image_modify.output,
469                &image_modify.constraint,
470                "Modify",
471            );
472        }
473
474        for image_modify in &node.image_copies {
475            propagate_image_constraints_forward(
476                graph,
477                &mut image_version_states,
478                image_modify.input,
479                image_modify.output,
480                &image_modify.constraint,
481                "Modify",
482            );
483        }
484
485        //
486        // Propagate constraints forward for buffers being modified.
487        //
488        for buffer_modify in &node.buffer_modifies {
489            propagate_buffer_constraints_forward(
490                graph,
491                &mut buffer_version_states,
492                buffer_modify.input,
493                buffer_modify.output,
494                &buffer_modify.constraint,
495                "Modify",
496            );
497        }
498
499        for buffer_modify in &node.buffer_copies {
500            propagate_buffer_constraints_forward(
501                graph,
502                &mut buffer_version_states,
503                buffer_modify.input,
504                buffer_modify.output,
505                &buffer_modify.constraint,
506                "Modify",
507            );
508        }
509    }
510
511    log::trace!("  Set up output images");
512
513    //
514    // Propagate output image state specifications into images
515    //
516    for external_image in &graph.external_images {
517        if let Some(output_usage) = external_image.output_usage {
518            log::trace!(
519                "    Image {:?} {:?}",
520                output_usage,
521                graph.image_resource(output_usage).name
522            );
523            let output_image_version_state = image_version_states
524                .entry(graph.image_version_create_usage(output_usage))
525                .or_default();
526            let output_constraint = external_image.specification.clone().into();
527            output_image_version_state.partial_merge(&output_constraint);
528
529            image_version_states.insert(output_usage, external_image.specification.clone().into());
530        }
531    }
532
533    //
534    // Propagate output buffer state specifications into buffers
535    //
536    for external_buffer in &graph.external_buffers {
537        if let Some(output_usage) = external_buffer.output_usage {
538            log::trace!(
539                "    Buffer {:?} {:?}",
540                output_usage,
541                graph.buffer_resource(output_usage).name
542            );
543            let output_buffer_version_state = buffer_version_states
544                .entry(graph.buffer_version_create_usage(output_usage))
545                .or_default();
546            let output_constraint = external_buffer.specification.clone().into();
547            output_buffer_version_state.partial_merge(&output_constraint);
548
549            buffer_version_states
550                .insert(output_usage, external_buffer.specification.clone().into());
551        }
552    }
553
554    log::trace!("  Propagate constraints BACKWARD");
555
556    //
557    // Iterate backwards through nodes, determining the state the image must be in at every
558    // step
559    //
560    for node_id in node_execution_order.iter().rev() {
561        let node = graph.node(*node_id);
562        log::trace!("    node {:?} {:?}", node_id, node.name());
563
564        // Don't need to worry about creates, we back propagate to them when reading/modifying
565
566        //
567        // Propagate backwards from reads
568        //
569        for image_read in &node.image_reads {
570            log::trace!(
571                "      Read image {:?} {:?}",
572                image_read.image,
573                graph.image_resource(image_read.image).name
574            );
575
576            let version_state = image_version_states
577                .entry(graph.image_version_create_usage(image_read.image))
578                .or_default();
579            version_state.partial_merge(&image_read.constraint);
580
581            // If this is an image read with no output, it's possible the constraint on the read is incomplete.
582            // So we need to merge the image state that may have information forward-propagated
583            // into it with the constraints on the read. (Conceptually it's like we're forward
584            // propagating here because the main forward propagate pass does not handle reads.
585            // TODO: We could consider moving this to the forward pass
586            let mut image_read_constraint = image_read.constraint.clone();
587            image_read_constraint.partial_merge(&version_state);
588            log::trace!(
589                "        Read constraints will be {:?}",
590                image_read_constraint
591            );
592            if let Some(spec) =
593                image_read_constraint.try_convert_to_specification(swapchain_surface_info)
594            {
595                image_version_states.insert(image_read.image, spec.into());
596            } else {
597                panic!(
598                    "Not enough information in the graph to determine the specification for image {:?} {:?} being read by node {:?} {:?}. Constraints are: {:?}",
599                    image_read.image,
600                    graph.image_resource(image_read.image).name,
601                    node.id(),
602                    node.name(),
603                    image_version_states.get(&image_read.image)
604                );
605            }
606        }
607
608        //
609        // Propagate backwards from reads
610        //
611        for buffer_read in &node.buffer_reads {
612            log::trace!(
613                "      Read buffer {:?} {:?}",
614                buffer_read.buffer,
615                graph.buffer_resource(buffer_read.buffer).name
616            );
617
618            let version_state = buffer_version_states
619                .entry(graph.buffer_version_create_usage(buffer_read.buffer))
620                .or_default();
621            version_state.partial_merge(&buffer_read.constraint);
622
623            // If this is a buffer read with no output, it's possible the constraint on the read is incomplete.
624            // So we need to merge the buffer state that may have information forward-propagated
625            // into it with the constraints on the read. (Conceptually it's like we're forward
626            // propagating here because the main forward propagate pass does not handle reads.
627            // TODO: We could consider moving this to the forward pass
628            let mut buffer_read_constraint = buffer_read.constraint.clone();
629            buffer_read_constraint.partial_merge(&version_state);
630            log::trace!(
631                "        Read constraints will be {:?}",
632                buffer_read_constraint
633            );
634            if let Some(spec) = buffer_read_constraint.try_convert_to_specification() {
635                buffer_version_states.insert(buffer_read.buffer, spec.into());
636            } else {
637                panic!(
638                    "Not enough information in the graph to determine the specification for buffer {:?} {:?} being read by node {:?} {:?}. Constraints are: {:?}",
639                    buffer_read.buffer,
640                    graph.buffer_resource(buffer_read.buffer).name,
641                    node.id(),
642                    node.name(),
643                    buffer_version_states.get(&buffer_read.buffer)
644                );
645            }
646        }
647
648        fn propagate_image_constraints_backward(
649            graph: &RenderGraphBuilder,
650            image_version_states: &mut FnvHashMap<
651                RenderGraphImageUsageId,
652                RenderGraphImageConstraint,
653            >,
654            input: RenderGraphImageUsageId,
655            output: RenderGraphImageUsageId,
656            operation_name: &str,
657        ) {
658            log::trace!(
659                "      {} image {:?} {:?} <- {:?} {:?}",
660                operation_name,
661                input,
662                graph.image_resource(input).name,
663                output,
664                graph.image_resource(output).name
665            );
666            // The output image constraint already takes constraint into account from
667            // when we propagated image constraints forward
668            let output_image_constraint = image_version_states
669                .entry(graph.image_version_create_usage(output))
670                .or_default()
671                .clone();
672            let input_state = image_version_states
673                .entry(graph.image_version_create_usage(input))
674                .or_default();
675            input_state.partial_merge(&output_image_constraint);
676
677            image_version_states.insert(input, output_image_constraint.clone());
678        }
679
680        fn propagate_buffer_constraints_backward(
681            graph: &RenderGraphBuilder,
682            buffer_version_states: &mut FnvHashMap<
683                RenderGraphBufferUsageId,
684                RenderGraphBufferConstraint,
685            >,
686            input: RenderGraphBufferUsageId,
687            output: RenderGraphBufferUsageId,
688            operation_name: &str,
689        ) {
690            log::trace!(
691                "      {} buffer {:?} {:?} <- {:?} {:?}",
692                operation_name,
693                input,
694                graph.buffer_resource(input).name,
695                output,
696                graph.buffer_resource(output).name
697            );
698            // The output buffer constraint already takes constraint into account from
699            // when we propagated buffer constraints forward
700            let output_buffer_constraint = buffer_version_states
701                .entry(graph.buffer_version_create_usage(output))
702                .or_default()
703                .clone();
704            let input_state = buffer_version_states
705                .entry(graph.buffer_version_create_usage(input))
706                .or_default();
707            input_state.partial_merge(&output_buffer_constraint);
708
709            buffer_version_states.insert(input, output_buffer_constraint.clone());
710        }
711
712        //
713        // Propagate backwards from modifies
714        //
715        for image_modify in &node.image_modifies {
716            propagate_image_constraints_backward(
717                graph,
718                &mut image_version_states,
719                image_modify.input,
720                image_modify.output,
721                "Modify",
722            );
723        }
724
725        for image_copy in &node.image_copies {
726            propagate_image_constraints_backward(
727                graph,
728                &mut image_version_states,
729                image_copy.input,
730                image_copy.output,
731                "Copy",
732            );
733        }
734
735        for buffer_modify in &node.buffer_modifies {
736            propagate_buffer_constraints_backward(
737                graph,
738                &mut buffer_version_states,
739                buffer_modify.input,
740                buffer_modify.output,
741                "Modify",
742            );
743        }
744
745        for buffer_copy in &node.buffer_copies {
746            propagate_buffer_constraints_backward(
747                graph,
748                &mut buffer_version_states,
749                buffer_copy.input,
750                buffer_copy.output,
751                "Copy",
752            );
753        }
754    }
755
756    let mut image_specs = FnvHashMap::default();
757    for (k, v) in image_version_states {
758        image_specs.insert(
759            k,
760            v.try_convert_to_specification(swapchain_surface_info)
761                .unwrap(),
762        );
763    }
764
765    let mut buffer_specs = FnvHashMap::default();
766    for (k, v) in buffer_version_states {
767        buffer_specs.insert(k, v.try_convert_to_specification().unwrap());
768    }
769
770    DetermineConstraintsResult {
771        images: image_specs,
772        buffers: buffer_specs,
773    }
774}
775
776//
777// This function finds places where an image needs to transition from multisampled to non-multisampled.
778// This can be done efficiently by adding a resolve attachment to the pass. These resolves are
779// automatically inserted. This only works for color attachments (limitation of vulkan)
780//
781#[profiling::function]
782fn insert_resolves(
783    graph: &mut RenderGraphBuilder,
784    node_execution_order: &[RenderGraphNodeId],
785    constraint_results: &mut DetermineConstraintsResult,
786) {
787    log::trace!("Insert resolves in graph where necessary");
788    for node_id in node_execution_order {
789        let mut resolves_to_add = Vec::default();
790
791        let node = graph.node(*node_id);
792        log::trace!("  node {:?} {:?}", node_id, node.name);
793        // Iterate through all color attachments
794        for (color_attachment_index, color_attachment) in node.color_attachments.iter().enumerate()
795        {
796            if let Some(color_attachment) = color_attachment {
797                log::trace!("    color attachment {}", color_attachment_index);
798                // If this color attachment outputs an image
799                if let Some(write_image) = color_attachment.write_image {
800                    //let write_version = graph.image_usages[write_image.0].version;
801                    // Skip if it's not an MSAA image
802                    let write_spec = constraint_results.image_specification(write_image).unwrap();
803                    if write_spec.samples == RafxSampleCount::SampleCount1 {
804                        log::trace!("      already non-MSAA");
805                        continue;
806                    }
807
808                    // Calculate the spec that we would have after the resolve
809                    let mut resolve_spec = write_spec.clone();
810                    resolve_spec.samples = RafxSampleCount::SampleCount1;
811
812                    let mut usages_to_move = vec![];
813
814                    // Look for any usages we need to fix
815                    for (usage_index, read_usage) in graph
816                        .image_version_info(write_image)
817                        .read_usages
818                        .iter()
819                        .enumerate()
820                    {
821                        log::trace!(
822                            "      usage {}, {:?} {:?}",
823                            usage_index,
824                            read_usage,
825                            graph.image_usages[read_usage.0].usage_type
826                        );
827                        let read_spec =
828                            constraint_results.image_specification(*read_usage).unwrap();
829                        if *read_spec == *write_spec {
830                            continue;
831                        } else if *read_spec == resolve_spec {
832                            usages_to_move.push(*read_usage);
833                        } else {
834                            log::trace!(
835                                "        incompatibility cannot be fixed via renderpass resolve"
836                            );
837                            log::trace!("          resolve: {:?}", resolve_spec);
838                            log::trace!("          read   : {:?}", read_spec);
839                        }
840                    }
841
842                    if !usages_to_move.is_empty() {
843                        resolves_to_add.push((
844                            color_attachment_index,
845                            resolve_spec,
846                            usages_to_move,
847                        ));
848                    }
849                }
850            }
851        }
852
853        for (resolve_attachment_index, resolve_spec, usages_to_move) in resolves_to_add {
854            log::trace!(
855                "        ADDING RESOLVE FOR NODE {:?} ATTACHMENT {}",
856                node_id,
857                resolve_attachment_index
858            );
859            let image = graph.create_resolve_attachment(
860                *node_id,
861                resolve_attachment_index,
862                resolve_spec.clone().into(),
863                Default::default(),
864            );
865            constraint_results.images.insert(image, resolve_spec);
866
867            for usage in usages_to_move {
868                let from = graph.image_usages[usage.0].version;
869                let to = graph.image_usages[image.0].version;
870                log::trace!(
871                    "          MOVE USAGE {:?} from {:?} to {:?}",
872                    usage,
873                    from,
874                    to
875                );
876                graph.redirect_image_usage(usage, from, to)
877            }
878        }
879    }
880}
881
882/// Assignment of usages to actual images. This allows a single image to be passed through a
883/// sequence of reads and writes
884#[derive(Debug)]
885pub struct AssignVirtualResourcesResult {
886    image_usage_to_virtual: FnvHashMap<RenderGraphImageUsageId, VirtualImageId>,
887    buffer_usage_to_virtual: FnvHashMap<RenderGraphBufferUsageId, VirtualBufferId>,
888}
889
890#[derive(Default)]
891struct VirtualImageIdAllocator {
892    next_id: usize,
893}
894
895impl VirtualImageIdAllocator {
896    fn allocate(&mut self) -> VirtualImageId {
897        let id = VirtualImageId(self.next_id);
898        self.next_id += 1;
899        id
900    }
901}
902
903#[derive(Default)]
904struct VirtualBufferIdAllocator {
905    next_id: usize,
906}
907
908impl VirtualBufferIdAllocator {
909    fn allocate(&mut self) -> VirtualBufferId {
910        let id = VirtualBufferId(self.next_id);
911        self.next_id += 1;
912        id
913    }
914}
915
916//
917// The graph is built with the assumption that every image is immutable. However in most cases we
918// can easily pass the same image through multiple passes saving memory and the need to copy data.
919// This function finds places where we can trivially forward an image from one pass to another. In
920// the future, cases where this is not possible might be handled by copying the image. (Needed if
921// there are multiple downstream consumers modifying the image or if the format needs to change.
922//
923#[profiling::function]
924fn assign_virtual_resources(
925    graph: &RenderGraphBuilder,
926    node_execution_order: &[RenderGraphNodeId],
927    constraint_results: &mut DetermineConstraintsResult,
928) -> AssignVirtualResourcesResult {
929    let mut image_usage_to_virtual: FnvHashMap<RenderGraphImageUsageId, VirtualImageId> =
930        FnvHashMap::default();
931    let mut buffer_usage_to_virtual: FnvHashMap<RenderGraphBufferUsageId, VirtualBufferId> =
932        FnvHashMap::default();
933
934    let mut virtual_image_id_allocator = VirtualImageIdAllocator::default();
935    let mut virtual_buffer_id_allocator = VirtualBufferIdAllocator::default();
936
937    log::trace!("Associate input images with virtual images");
938    for external_image in &graph.external_images {
939        if let Some(input_usage) = external_image.input_usage {
940            // Assign the image
941            let virtual_image = virtual_image_id_allocator.allocate();
942            log::trace!(
943                "    External image {:?} used as input will use image {:?}",
944                input_usage,
945                virtual_image
946            );
947            image_usage_to_virtual.insert(input_usage, virtual_image);
948
949            // Try to share the image forward to downstream consumers
950            propagate_virtual_image_id(
951                graph,
952                constraint_results,
953                &mut image_usage_to_virtual,
954                &mut virtual_image_id_allocator,
955                input_usage,
956            );
957        }
958    }
959
960    log::trace!("Associate input buffers with virtual buffers");
961    for external_buffer in &graph.external_buffers {
962        if let Some(input_usage) = external_buffer.input_usage {
963            // Assign the buffer
964            let virtual_buffer = virtual_buffer_id_allocator.allocate();
965            log::trace!(
966                "    External buffer {:?} used as input will use buffer {:?}",
967                external_buffer.external_buffer_id,
968                virtual_buffer
969            );
970            buffer_usage_to_virtual.insert(input_usage, virtual_buffer);
971
972            // Try to share the buffer forward to downstream consumers
973            propagate_virtual_buffer_id(
974                graph,
975                constraint_results,
976                &mut buffer_usage_to_virtual,
977                &mut virtual_buffer_id_allocator,
978                input_usage,
979            );
980        }
981    }
982
983    //TODO: Associate input images here? We can wait until we decide which images are shared
984    log::trace!("Associate images written by nodes with virtual images");
985    for node in node_execution_order.iter() {
986        let node = graph.node(*node);
987        log::trace!("  node {:?} {:?}", node.id().0, node.name());
988
989        // A list of all images we write to from this node. We will try to share the images
990        // being written forward into the nodes of downstream reads. This can chain such that
991        // the same image is shared by many nodes
992
993        //
994        // Handle images created by this node
995        //
996        for image_create in &node.image_creates {
997            // An image that's created always allocates an image (we reuse these if they are compatible
998            // and lifetimes don't overlap)
999            let virtual_image = virtual_image_id_allocator.allocate();
1000            log::trace!(
1001                "    Create {:?} will use image {:?}",
1002                image_create.image,
1003                virtual_image
1004            );
1005            image_usage_to_virtual.insert(image_create.image, virtual_image);
1006            // Queue this image write to try to share the image forward
1007            propagate_virtual_image_id(
1008                graph,
1009                constraint_results,
1010                &mut image_usage_to_virtual,
1011                &mut virtual_image_id_allocator,
1012                image_create.image,
1013            );
1014        }
1015
1016        //
1017        // Handle buffers created by this node
1018        //
1019        for buffer_create in &node.buffer_creates {
1020            // A buffer that's created always allocates a buffer (we reuse these if they are compatible
1021            // and lifetimes don't overlap)
1022            let virtual_buffer = virtual_buffer_id_allocator.allocate();
1023            log::trace!(
1024                "    Create {:?} will use buffer {:?}",
1025                buffer_create.buffer,
1026                virtual_buffer
1027            );
1028            buffer_usage_to_virtual.insert(buffer_create.buffer, virtual_buffer);
1029            // Try to share the buffer forward to downstream consumers
1030            propagate_virtual_buffer_id(
1031                graph,
1032                constraint_results,
1033                &mut buffer_usage_to_virtual,
1034                &mut virtual_buffer_id_allocator,
1035                buffer_create.buffer,
1036            );
1037        }
1038
1039        //
1040        // Handle images modified by this node
1041        //
1042        for image_modify in &node.image_modifies {
1043            // The virtual image in the read portion of a image_modify must also be the write image.
1044            // The format of the input/output is guaranteed to match
1045            assert_eq!(
1046                constraint_results.image_specification(image_modify.input),
1047                constraint_results.image_specification(image_modify.output)
1048            );
1049
1050            // Assign the image
1051            let virtual_image = *image_usage_to_virtual.get(&image_modify.input).unwrap();
1052            log::trace!(
1053                "    Modify {:?} will pass through image {:?}",
1054                image_modify.output,
1055                virtual_image
1056            );
1057            image_usage_to_virtual.insert(image_modify.output, virtual_image);
1058
1059            // Queue this image write to try to share the image forward
1060            propagate_virtual_image_id(
1061                graph,
1062                constraint_results,
1063                &mut image_usage_to_virtual,
1064                &mut virtual_image_id_allocator,
1065                image_modify.output,
1066            );
1067        }
1068
1069        //
1070        // Handle buffers modified by this node
1071        //
1072        for buffer_modify in &node.buffer_modifies {
1073            // The virtual buffer in the read portion of a buffer_modify must also be the write buffer.
1074            // The format of the input/output is guaranteed to match
1075            assert_eq!(
1076                constraint_results.buffer_specification(buffer_modify.input),
1077                constraint_results.buffer_specification(buffer_modify.output)
1078            );
1079
1080            // Assign the buffer
1081            let virtual_buffer = *buffer_usage_to_virtual.get(&buffer_modify.input).unwrap();
1082            log::trace!(
1083                "    Modify {:?} will pass through buffer {:?}",
1084                buffer_modify.output,
1085                virtual_buffer
1086            );
1087            buffer_usage_to_virtual.insert(buffer_modify.output, virtual_buffer);
1088
1089            // Try to share the buffer forward to downstream consumers
1090            propagate_virtual_buffer_id(
1091                graph,
1092                constraint_results,
1093                &mut buffer_usage_to_virtual,
1094                &mut virtual_buffer_id_allocator,
1095                buffer_modify.output,
1096            );
1097        }
1098    }
1099
1100    // vulkan image layouts: https://github.com/nannou-org/nannou/issues/271#issuecomment-465876622
1101    AssignVirtualResourcesResult {
1102        image_usage_to_virtual,
1103        buffer_usage_to_virtual,
1104    }
1105}
1106
1107fn propagate_virtual_image_id(
1108    graph: &RenderGraphBuilder,
1109    constraint_results: &DetermineConstraintsResult,
1110    image_usage_to_virtual: &mut FnvHashMap<RenderGraphImageUsageId, VirtualImageId>,
1111    virtual_image_id_allocator: &mut VirtualImageIdAllocator,
1112    written_image: RenderGraphImageUsageId,
1113) {
1114    // Count the downstream users of this image based on if they need read-only access
1115    // or write access. We need this information to determine which usages we can share
1116    // the output data with.
1117    //
1118    // I'm not sure if this works as written. I was thinking we might have trouble with
1119    // multiple readers, and then they pass to a writer, but now that I think of it, readers
1120    // don't "output" anything.
1121    //
1122    // That said, this doesn't understand multiple writers of different subresources right
1123    // now.
1124    //
1125    //TODO: This could be smarter to handle the case of a resource being read and then
1126    // later written
1127    //TODO: Could handle non-overlapping subresource ranges being written
1128    let written_image_version_info = graph.image_version_info(written_image);
1129    let mut read_count = 0;
1130    //let mut read_ranges = vec![];
1131    let mut write_count = 0;
1132    //let mut write_ranges = vec![];
1133    for usage in &written_image_version_info.read_usages {
1134        if graph.image_usages[usage.0].usage_type.is_read_only() {
1135            read_count += 1;
1136            //read_ranges.push(graph.image_usages[usage.0].subresource_range.clone());
1137        } else {
1138            write_count += 1;
1139            //write_ranges.push(graph.image_usages[usage.0].subresource_range.clone());
1140        }
1141    }
1142
1143    // let mut has_overlapping_write = false;
1144    // for i in 0..write_ranges.len() {
1145    //     for j in 0..i {
1146    //
1147    //     }
1148    // }
1149
1150    let write_virtual_image = *image_usage_to_virtual.get(&written_image).unwrap();
1151    let write_type = graph.image_usages[written_image.0].usage_type;
1152
1153    let written_spec = constraint_results
1154        .image_specification(written_image)
1155        .unwrap();
1156
1157    for usage_resource_id in &written_image_version_info.read_usages {
1158        let usage_spec = match constraint_results.image_specification(*usage_resource_id) {
1159            Some(usage_spec) => usage_spec,
1160            // If the reader of this image was culled, we may not have determined a spec.
1161            // If so, skip this usage
1162            None => continue,
1163        };
1164
1165        // We can't share images if they aren't the same format
1166        let specifications_compatible =
1167            RenderGraphImageSpecification::specifications_are_compatible(written_spec, usage_spec);
1168
1169        // We can't share images unless it's a read or it's an exclusive write
1170        let is_read_or_exclusive_write = (read_count > 0
1171            && graph.image_usages[usage_resource_id.0]
1172                .usage_type
1173                .is_read_only())
1174            || write_count <= 1;
1175
1176        let read_type = graph.image_usages[usage_resource_id.0].usage_type;
1177        if specifications_compatible && is_read_or_exclusive_write {
1178            // it's a shared read or an exclusive write
1179            log::trace!(
1180                "    Usage {:?} will share an image with {:?} ({:?} -> {:?})",
1181                written_image,
1182                usage_resource_id,
1183                write_type,
1184                read_type
1185            );
1186            let overwritten_image =
1187                image_usage_to_virtual.insert(*usage_resource_id, write_virtual_image);
1188
1189            assert!(overwritten_image.is_none());
1190        } else {
1191            // allocate new image
1192            let virtual_image = virtual_image_id_allocator.allocate();
1193            log::info!(
1194                "    Allocate image {:?} for {:?} ({:?} -> {:?})  (specifications_compatible match: {} is_read_or_exclusive_write: {})",
1195                virtual_image,
1196                usage_resource_id,
1197                write_type,
1198                read_type,
1199                specifications_compatible,
1200                is_read_or_exclusive_write
1201            );
1202            let overwritten_image =
1203                image_usage_to_virtual.insert(*usage_resource_id, virtual_image);
1204
1205            assert!(overwritten_image.is_none());
1206
1207            //TODO: One issue (aside from not doing any blits right now) is that images created in this way
1208            // aren't included in the assign_physical_images logic
1209
1210            log::info!(
1211                "      writer     : {}",
1212                graph.debug_user_name_of_image_usage(written_image)
1213            );
1214            log::info!(
1215                "      reader     : {}",
1216                graph.debug_user_name_of_image_usage(*usage_resource_id)
1217            );
1218
1219            if !specifications_compatible {
1220                log::info!("      writer spec: {:?}", written_spec);
1221                log::info!("      reader spec: {:?}", usage_spec);
1222            }
1223            log::info!("      --- All Usages ---");
1224            log::info!(
1225                "      Creator: {} spec: {:?}",
1226                graph.debug_user_name_of_image_usage(written_image),
1227                constraint_results.image_specification(written_image)
1228            );
1229            for &read_usage in &written_image_version_info.read_usages {
1230                log::info!(
1231                    "        Reader: {} spec: {:?}",
1232                    graph.debug_user_name_of_image_usage(read_usage),
1233                    constraint_results.image_specification(read_usage)
1234                );
1235            }
1236
1237            panic!("The render graph contains an image conflict that cannot be automatically resolved.");
1238        }
1239    }
1240}
1241
1242fn propagate_virtual_buffer_id(
1243    graph: &RenderGraphBuilder,
1244    constraint_results: &DetermineConstraintsResult,
1245    buffer_usage_to_virtual: &mut FnvHashMap<RenderGraphBufferUsageId, VirtualBufferId>,
1246    virtual_buffer_id_allocator: &mut VirtualBufferIdAllocator,
1247    written_buffer: RenderGraphBufferUsageId,
1248) {
1249    // Count the downstream users of this image based on if they need read-only access
1250    // or write access. We need this information to determine which usages we can share
1251    // the output data with.
1252    //
1253    // I'm not sure if this works as written. I was thinking we might have trouble with
1254    // multiple readers, and then they pass to a writer, but now that I think of it, readers
1255    // don't "output" anything.
1256    //TODO: This could be smarter to handle the case of a resource being read and then
1257    // later written
1258
1259    let written_buffer_version_info = graph.buffer_version_info(written_buffer);
1260    let mut read_count = 0;
1261    let mut write_count = 0;
1262    for usage in &written_buffer_version_info.read_usages {
1263        if graph.buffer_usages[usage.0].usage_type.is_read_only() {
1264            read_count += 1;
1265        } else {
1266            write_count += 1;
1267        }
1268    }
1269
1270    let write_virtual_buffer = *buffer_usage_to_virtual.get(&written_buffer).unwrap();
1271    let write_type = graph.buffer_usages[written_buffer.0].usage_type;
1272
1273    let written_spec = constraint_results
1274        .buffer_specification(written_buffer)
1275        .unwrap();
1276
1277    for usage_resource_id in &written_buffer_version_info.read_usages {
1278        let usage_spec = match constraint_results.buffer_specification(*usage_resource_id) {
1279            Some(usage_spec) => usage_spec,
1280            // If the reader of this buffer was culled, we may not have determined a spec.
1281            // If so, skip this usage
1282            None => continue,
1283        };
1284
1285        // We can't share buffers if they aren't the same format
1286        let specifications_compatible =
1287            RenderGraphBufferSpecification::specifications_are_compatible(written_spec, usage_spec);
1288
1289        // We can't share buffers unless it's a read or it's an exclusive write
1290        let is_read_or_exclusive_write = (read_count > 0
1291            && graph.buffer_usages[usage_resource_id.0]
1292                .usage_type
1293                .is_read_only())
1294            || write_count <= 1;
1295
1296        let read_type = graph.buffer_usages[usage_resource_id.0].usage_type;
1297        if specifications_compatible && is_read_or_exclusive_write {
1298            // it's a shared read or an exclusive write
1299            log::trace!(
1300                "    Usage {:?} will share a buffer with {:?} ({:?} -> {:?})",
1301                written_buffer,
1302                usage_resource_id,
1303                write_type,
1304                read_type
1305            );
1306            let overwritten_buffer =
1307                buffer_usage_to_virtual.insert(*usage_resource_id, write_virtual_buffer);
1308
1309            assert!(overwritten_buffer.is_none());
1310        } else {
1311            // allocate new buffer
1312            let virtual_buffer = virtual_buffer_id_allocator.allocate();
1313            log::info!(
1314                "    Allocate buffer {:?} for {:?} ({:?} -> {:?})  (specifications_compatible match: {} is_read_or_exclusive_write: {})",
1315                virtual_buffer,
1316                usage_resource_id,
1317                write_type,
1318                read_type,
1319                specifications_compatible,
1320                is_read_or_exclusive_write
1321            );
1322
1323            let overwritten_buffer =
1324                buffer_usage_to_virtual.insert(*usage_resource_id, virtual_buffer);
1325
1326            assert!(overwritten_buffer.is_none());
1327
1328            //TODO: One issue (aside from not doing any copies right now) is that buffers created in this way
1329            // aren't included in the assign_physical_buffers logic
1330
1331            log::info!(
1332                "      writer     : {}",
1333                graph.debug_user_name_of_buffer_usage(written_buffer)
1334            );
1335            log::info!(
1336                "      reader     : {}",
1337                graph.debug_user_name_of_buffer_usage(*usage_resource_id)
1338            );
1339
1340            if !specifications_compatible {
1341                log::info!("      writer spec: {:?}", written_spec);
1342                log::info!("      reader spec: {:?}", usage_spec);
1343            }
1344            log::info!("      --- All Usages ---");
1345            log::info!(
1346                "      Creator: {} spec {:?}",
1347                graph.debug_user_name_of_buffer_usage(written_buffer),
1348                constraint_results.buffer_specification(written_buffer)
1349            );
1350            for &read_usage in &written_buffer_version_info.read_usages {
1351                log::info!(
1352                    "        Reader: {} spec {:?}",
1353                    graph.debug_user_name_of_buffer_usage(read_usage),
1354                    constraint_results.buffer_specification(read_usage)
1355                );
1356            }
1357
1358            panic!("The render graph contains a buffer conflict that cannot be automatically resolved.");
1359        }
1360    }
1361}
1362
1363//
1364// This walks through the nodes and creates passes/subpasses. Most of the info to create them is
1365// determined here along with stage/access/queue family barrier info. (The barrier info is used
1366// later.. some of the invalidates/flushes can be merged.)
1367//
1368#[profiling::function]
1369fn build_physical_passes(
1370    graph: &RenderGraphBuilder,
1371    node_execution_order: &[RenderGraphNodeId],
1372    constraints: &DetermineConstraintsResult,
1373    virtual_resources: &AssignVirtualResourcesResult,
1374) -> Vec<RenderGraphPass> {
1375    #[derive(Debug)]
1376    enum PassNode {
1377        RenderpassNode(RenderGraphNodeId),
1378        CallbackNode(RenderGraphNodeId),
1379    }
1380
1381    // All passes
1382    let mut pass_nodes = Vec::default();
1383
1384    for node_id in node_execution_order {
1385        let pass_node = match graph.node(*node_id).kind {
1386            RenderGraphNodeKind::Renderpass => PassNode::RenderpassNode(*node_id),
1387            RenderGraphNodeKind::Callback => PassNode::CallbackNode(*node_id),
1388        };
1389        pass_nodes.push(pass_node);
1390    }
1391
1392    log::trace!("gather pass info");
1393    let mut passes = Vec::default();
1394    for pass_node in pass_nodes {
1395        log::trace!("  nodes in pass: {:?}", pass_node);
1396
1397        fn find_or_insert_attachment(
1398            attachments: &mut Vec<RenderGraphPassAttachment>,
1399            usage: RenderGraphImageUsageId,
1400            virtual_image: VirtualImageId,
1401        ) -> (usize, bool) {
1402            if let Some(position) = attachments
1403                .iter()
1404                .position(|x| x.virtual_image == virtual_image)
1405            {
1406                (position, false)
1407            } else {
1408                attachments.push(RenderGraphPassAttachment {
1409                    usage,
1410                    virtual_image,
1411
1412                    //NOTE: These get assigned later in assign_physical_images
1413                    image: None,
1414                    image_view: None,
1415
1416                    load_op: RafxLoadOp::DontCare,
1417                    stencil_load_op: RafxLoadOp::DontCare,
1418                    store_op: RafxStoreOp::DontCare,
1419                    stencil_store_op: RafxStoreOp::DontCare,
1420                    clear_color: None,
1421                    format: RafxFormat::UNDEFINED,
1422                    samples: RafxSampleCount::SampleCount1,
1423
1424                    // NOTE: These get assigned later in build_pass_barriers
1425                    initial_state: RafxResourceState::UNDEFINED,
1426                    final_state: RafxResourceState::UNDEFINED,
1427                });
1428                (attachments.len() - 1, true)
1429            }
1430        }
1431
1432        match pass_node {
1433            PassNode::CallbackNode(compute_node) => {
1434                passes.push(RenderGraphPass::Callback(RenderGraphCallbackPass {
1435                    node: compute_node,
1436                    pre_pass_barrier: Default::default(),
1437                }));
1438            }
1439            PassNode::RenderpassNode(renderpass_node) => {
1440                let mut renderpass_attachments = Vec::default();
1441
1442                log::trace!("    subpass node: {:?}", renderpass_node);
1443                let subpass_node = graph.node(renderpass_node);
1444
1445                // Don't create a subpass if there are no attachments
1446                if subpass_node.color_attachments.is_empty()
1447                    && subpass_node.depth_attachment.is_none()
1448                {
1449                    assert!(subpass_node.resolve_attachments.is_empty());
1450                    log::trace!("      Not generating a subpass - no attachments");
1451                    continue;
1452                }
1453
1454                let mut pass_color_attachments: [Option<usize>; MAX_COLOR_ATTACHMENTS] =
1455                    Default::default();
1456                let mut pass_resolve_attachments: [Option<usize>; MAX_COLOR_ATTACHMENTS] =
1457                    Default::default();
1458                let mut pass_depth_attachment = Default::default();
1459
1460                for (color_attachment_index, color_attachment) in
1461                    subpass_node.color_attachments.iter().enumerate()
1462                {
1463                    if let Some(color_attachment) = color_attachment {
1464                        let read_or_write_usage = color_attachment
1465                            .read_image
1466                            .or(color_attachment.write_image)
1467                            .unwrap();
1468                        let virtual_image = virtual_resources
1469                            .image_usage_to_virtual
1470                            .get(&read_or_write_usage)
1471                            .unwrap();
1472
1473                        let specification = constraints.images.get(&read_or_write_usage).unwrap();
1474                        log::trace!("      virtual attachment (color): {:?}", virtual_image);
1475
1476                        let (pass_attachment_index, is_first_usage) = find_or_insert_attachment(
1477                            &mut renderpass_attachments,
1478                            read_or_write_usage,
1479                            *virtual_image, /*, subresource_range*/
1480                        );
1481                        pass_color_attachments[color_attachment_index] =
1482                            Some(pass_attachment_index);
1483
1484                        let attachment = &mut renderpass_attachments[pass_attachment_index];
1485                        if is_first_usage {
1486                            // Check if we load or clear
1487                            if color_attachment.clear_color_value.is_some() {
1488                                attachment.load_op = RafxLoadOp::Clear;
1489                                attachment.clear_color = Some(AttachmentClearValue::Color(
1490                                    color_attachment.clear_color_value.unwrap(),
1491                                ))
1492                            } else if color_attachment.read_image.is_some() {
1493                                attachment.load_op = RafxLoadOp::Load;
1494                            }
1495
1496                            attachment.format = specification.format.into();
1497                            attachment.samples = specification.samples.into();
1498                        };
1499
1500                        let store_op = if let Some(write_image) = color_attachment.write_image {
1501                            if !graph.image_version_info(write_image).read_usages.is_empty() {
1502                                RafxStoreOp::Store
1503                            } else {
1504                                RafxStoreOp::DontCare
1505                            }
1506                        } else {
1507                            RafxStoreOp::DontCare
1508                        };
1509
1510                        attachment.store_op = store_op;
1511                        attachment.stencil_store_op = RafxStoreOp::DontCare;
1512                    }
1513                }
1514
1515                for (resolve_attachment_index, resolve_attachment) in
1516                    subpass_node.resolve_attachments.iter().enumerate()
1517                {
1518                    if let Some(resolve_attachment) = resolve_attachment {
1519                        let write_image = resolve_attachment.write_image;
1520                        let virtual_image = virtual_resources
1521                            .image_usage_to_virtual
1522                            .get(&write_image)
1523                            .unwrap();
1524                        //let version_id = graph.image_version_id(write_image);
1525                        let specification = constraints.images.get(&write_image).unwrap();
1526                        log::trace!("      virtual attachment (resolve): {:?}", virtual_image);
1527
1528                        let (pass_attachment_index, is_first_usage) = find_or_insert_attachment(
1529                            &mut renderpass_attachments,
1530                            write_image,
1531                            *virtual_image, /*, subresource_range*/
1532                        );
1533                        pass_resolve_attachments[resolve_attachment_index] =
1534                            Some(pass_attachment_index);
1535
1536                        assert!(is_first_usage); // Not sure if this assert is valid
1537                        let attachment = &mut renderpass_attachments[pass_attachment_index];
1538                        attachment.format = specification.format.into();
1539                        attachment.samples = specification.samples.into();
1540
1541                        //TODO: Should we skip resolving if there is no reader?
1542                        let store_op =
1543                            if !graph.image_version_info(write_image).read_usages.is_empty() {
1544                                RafxStoreOp::Store
1545                            } else {
1546                                RafxStoreOp::DontCare
1547                            };
1548
1549                        attachment.store_op = store_op;
1550                        attachment.stencil_store_op = RafxStoreOp::DontCare;
1551                    }
1552                }
1553
1554                if let Some(depth_attachment) = &subpass_node.depth_attachment {
1555                    let read_or_write_usage = depth_attachment
1556                        .read_image
1557                        .or(depth_attachment.write_image)
1558                        .unwrap();
1559                    let virtual_image = virtual_resources
1560                        .image_usage_to_virtual
1561                        .get(&read_or_write_usage)
1562                        .unwrap();
1563                    let specification = constraints.images.get(&read_or_write_usage).unwrap();
1564                    log::trace!("      virtual attachment (depth): {:?}", virtual_image);
1565
1566                    let (pass_attachment_index, is_first_usage) = find_or_insert_attachment(
1567                        &mut renderpass_attachments,
1568                        read_or_write_usage,
1569                        *virtual_image, /*, subresource_range*/
1570                    );
1571                    pass_depth_attachment = Some(pass_attachment_index);
1572
1573                    let attachment = &mut renderpass_attachments[pass_attachment_index];
1574                    if is_first_usage {
1575                        // Check if we load or clear
1576                        //TODO: Support load_op for stencil
1577
1578                        if depth_attachment.clear_depth_stencil_value.is_some() {
1579                            if depth_attachment.has_depth {
1580                                attachment.load_op = RafxLoadOp::Clear;
1581                            }
1582                            if depth_attachment.has_stencil {
1583                                attachment.stencil_load_op = RafxLoadOp::Clear;
1584                            }
1585                            attachment.clear_color = Some(AttachmentClearValue::DepthStencil(
1586                                depth_attachment.clear_depth_stencil_value.unwrap(),
1587                            ));
1588                        } else if depth_attachment.read_image.is_some() {
1589                            if depth_attachment.has_depth {
1590                                attachment.load_op = RafxLoadOp::Load;
1591                            }
1592
1593                            if depth_attachment.has_stencil {
1594                                attachment.stencil_load_op = RafxLoadOp::Load;
1595                            }
1596                        }
1597
1598                        attachment.format = specification.format.into();
1599                        attachment.samples = specification.samples.into();
1600                    };
1601
1602                    let store_op = if let Some(write_image) = depth_attachment.write_image {
1603                        if !graph.image_version_info(write_image).read_usages.is_empty() {
1604                            RafxStoreOp::Store
1605                        } else {
1606                            RafxStoreOp::DontCare
1607                        }
1608                    } else {
1609                        RafxStoreOp::DontCare
1610                    };
1611
1612                    if depth_attachment.has_depth {
1613                        attachment.store_op = store_op;
1614                    }
1615
1616                    if depth_attachment.has_stencil {
1617                        attachment.stencil_store_op = store_op;
1618                    }
1619                }
1620
1621                passes.push(RenderGraphPass::Render(RenderGraphRenderPass {
1622                    node_id: renderpass_node,
1623                    attachments: renderpass_attachments,
1624                    color_attachments: pass_color_attachments,
1625                    depth_attachment: pass_depth_attachment,
1626                    resolve_attachments: pass_resolve_attachments,
1627                    pre_pass_barrier: None,
1628                }));
1629            }
1630        }
1631    }
1632
1633    passes
1634}
1635
1636#[derive(Debug)]
1637struct AssignPhysicalResourcesResult {
1638    image_usage_to_physical: FnvHashMap<RenderGraphImageUsageId, PhysicalImageId>,
1639    image_usage_to_image_view: FnvHashMap<RenderGraphImageUsageId, PhysicalImageViewId>,
1640    image_views: Vec<RenderGraphImageView>, // indexed by physical image view id
1641    #[allow(unused)]
1642    image_virtual_to_physical: FnvHashMap<VirtualImageId, PhysicalImageId>,
1643    image_specifications: Vec<RenderGraphImageSpecification>, // indexed by physical image id
1644
1645    #[allow(unused)]
1646    buffer_usage_to_physical: FnvHashMap<RenderGraphBufferUsageId, PhysicalBufferId>,
1647    #[allow(unused)]
1648    buffer_virtual_to_physical: FnvHashMap<VirtualBufferId, PhysicalBufferId>,
1649    buffer_specifications: Vec<RenderGraphBufferSpecification>, // indexed by physical image id
1650}
1651
1652//
1653// This function walks through all the passes and creates a minimal list of images/buffers, potentially
1654// reusing a resource for multiple purposes during the execution of the graph. (Only if the lifetimes
1655// of those usages don't overlap!) For example, if we do a series of blurs, we can collapse those
1656// image usages into ping-ponging back and forth between two images.
1657//
1658#[profiling::function]
1659fn assign_physical_resources(
1660    graph: &RenderGraphBuilder,
1661    constraints: &DetermineConstraintsResult,
1662    virtual_resources: &AssignVirtualResourcesResult,
1663    passes: &mut [RenderGraphPass],
1664) -> AssignPhysicalResourcesResult {
1665    log::trace!("-- Assign physical resources --");
1666    struct PhysicalImageReuseRequirements {
1667        virtual_id: VirtualImageId,
1668        specification: RenderGraphImageSpecification,
1669        first_node_pass_index: usize,
1670        last_node_pass_index: usize,
1671    }
1672
1673    struct PhysicalBufferReuseRequirements {
1674        virtual_id: VirtualBufferId,
1675        specification: RenderGraphBufferSpecification,
1676        first_node_pass_index: usize,
1677        last_node_pass_index: usize,
1678    }
1679
1680    //
1681    // This inner function is responsible for populating reuse_requirements and
1682    // reuse_requirements_lookup. The goal here is to determine the lifetimes of all virtual images
1683    //
1684    fn add_or_modify_reuse_image_requirements(
1685        virtual_resources: &AssignVirtualResourcesResult,
1686        constraints: &DetermineConstraintsResult,
1687        pass_index: usize,
1688        usage: RenderGraphImageUsageId,
1689        reuse_requirements: &mut Vec<PhysicalImageReuseRequirements>,
1690        reuse_requirements_lookup: &mut FnvHashMap<VirtualImageId, usize>,
1691    ) {
1692        // Get physical ID from usage
1693        let virtual_id = virtual_resources.image_usage_to_virtual[&usage];
1694
1695        // Find requirements for this image if they exist, or create new requirements. This is a
1696        // lookup for an index so that the requirements will be stored sorted by
1697        // first_node_pass_index for iteration later
1698        let reused_image_requirements_index = *reuse_requirements_lookup
1699            .entry(virtual_id)
1700            .or_insert_with(|| {
1701                let reused_image_requirements_index = reuse_requirements.len();
1702                let specification = &constraints.images[&usage];
1703                reuse_requirements.push(PhysicalImageReuseRequirements {
1704                    virtual_id,
1705                    first_node_pass_index: pass_index,
1706                    last_node_pass_index: pass_index,
1707                    specification: specification.clone(),
1708                });
1709
1710                log::trace!("  Add requirement {:?} {:?}", virtual_id, specification);
1711                reused_image_requirements_index
1712            });
1713
1714        // Update the last pass index
1715        reuse_requirements[reused_image_requirements_index].last_node_pass_index = pass_index;
1716    }
1717
1718    fn add_or_modify_reuse_buffer_requirements(
1719        virtual_resources: &AssignVirtualResourcesResult,
1720        constraints: &DetermineConstraintsResult,
1721        pass_index: usize,
1722        usage: RenderGraphBufferUsageId,
1723        reuse_requirements: &mut Vec<PhysicalBufferReuseRequirements>,
1724        reuse_requirements_lookup: &mut FnvHashMap<VirtualBufferId, usize>,
1725    ) {
1726        // Get physical ID from usage
1727        let virtual_id = virtual_resources.buffer_usage_to_virtual[&usage];
1728
1729        // Find requirements for this buffer if they exist, or create new requirements. This is a
1730        // lookup for an index so that the requirements will be stored sorted by
1731        // first_node_pass_index for iteration later
1732        let reused_buffer_requirements_index = *reuse_requirements_lookup
1733            .entry(virtual_id)
1734            .or_insert_with(|| {
1735                let reused_buffer_requirements_index = reuse_requirements.len();
1736                let specification = &constraints.buffers[&usage];
1737                reuse_requirements.push(PhysicalBufferReuseRequirements {
1738                    virtual_id,
1739                    first_node_pass_index: pass_index,
1740                    last_node_pass_index: pass_index,
1741                    specification: specification.clone(),
1742                });
1743
1744                log::trace!("  Add requirement {:?} {:?}", virtual_id, specification);
1745                reused_buffer_requirements_index
1746            });
1747
1748        // Update the last pass index
1749        reuse_requirements[reused_buffer_requirements_index].last_node_pass_index = pass_index;
1750    }
1751
1752    let mut image_reuse_requirements = Vec::<PhysicalImageReuseRequirements>::default();
1753    let mut image_reuse_requirements_lookup = FnvHashMap::<VirtualImageId, usize>::default();
1754    let mut buffer_reuse_requirements = Vec::<PhysicalBufferReuseRequirements>::default();
1755    let mut buffer_reuse_requirements_lookup = FnvHashMap::<VirtualBufferId, usize>::default();
1756
1757    //
1758    // Walk through all image/buffer usages to determine their lifetimes
1759    //
1760    for (pass_index, pass) in passes.iter().enumerate() {
1761        let subpass_node_id = pass.node();
1762        let node = graph.node(subpass_node_id);
1763
1764        for image_modify in &node.image_modifies {
1765            add_or_modify_reuse_image_requirements(
1766                virtual_resources,
1767                constraints,
1768                pass_index,
1769                image_modify.input,
1770                &mut image_reuse_requirements,
1771                &mut image_reuse_requirements_lookup,
1772            );
1773            add_or_modify_reuse_image_requirements(
1774                virtual_resources,
1775                constraints,
1776                pass_index,
1777                image_modify.output,
1778                &mut image_reuse_requirements,
1779                &mut image_reuse_requirements_lookup,
1780            );
1781        }
1782
1783        for image_read in &node.image_reads {
1784            add_or_modify_reuse_image_requirements(
1785                virtual_resources,
1786                constraints,
1787                pass_index,
1788                image_read.image,
1789                &mut image_reuse_requirements,
1790                &mut image_reuse_requirements_lookup,
1791            );
1792        }
1793
1794        for image_create in &node.image_creates {
1795            add_or_modify_reuse_image_requirements(
1796                virtual_resources,
1797                constraints,
1798                pass_index,
1799                image_create.image,
1800                &mut image_reuse_requirements,
1801                &mut image_reuse_requirements_lookup,
1802            );
1803        }
1804
1805        for image_sample in &node.sampled_images {
1806            add_or_modify_reuse_image_requirements(
1807                virtual_resources,
1808                constraints,
1809                pass_index,
1810                *image_sample,
1811                &mut image_reuse_requirements,
1812                &mut image_reuse_requirements_lookup,
1813            );
1814        }
1815
1816        for buffer_modify in &node.buffer_modifies {
1817            add_or_modify_reuse_buffer_requirements(
1818                virtual_resources,
1819                constraints,
1820                pass_index,
1821                buffer_modify.input,
1822                &mut buffer_reuse_requirements,
1823                &mut buffer_reuse_requirements_lookup,
1824            );
1825            add_or_modify_reuse_buffer_requirements(
1826                virtual_resources,
1827                constraints,
1828                pass_index,
1829                buffer_modify.output,
1830                &mut buffer_reuse_requirements,
1831                &mut buffer_reuse_requirements_lookup,
1832            );
1833        }
1834
1835        for buffer_read in &node.buffer_reads {
1836            add_or_modify_reuse_buffer_requirements(
1837                virtual_resources,
1838                constraints,
1839                pass_index,
1840                buffer_read.buffer,
1841                &mut buffer_reuse_requirements,
1842                &mut buffer_reuse_requirements_lookup,
1843            );
1844        }
1845
1846        for buffer_create in &node.buffer_creates {
1847            add_or_modify_reuse_buffer_requirements(
1848                virtual_resources,
1849                constraints,
1850                pass_index,
1851                buffer_create.buffer,
1852                &mut buffer_reuse_requirements,
1853                &mut buffer_reuse_requirements_lookup,
1854            );
1855        }
1856    }
1857
1858    //TODO: Find transients
1859    //TODO: Mark input images as non-reuse?
1860    //TODO: Stay in same queue?
1861
1862    #[derive(Debug, PartialEq)]
1863    struct PhysicalImage {
1864        specification: RenderGraphImageSpecification,
1865        last_node_pass_index: usize,
1866        can_be_reused: bool,
1867    }
1868
1869    #[derive(Debug, PartialEq)]
1870    struct PhysicalBuffer {
1871        specification: RenderGraphBufferSpecification,
1872        last_node_pass_index: usize,
1873        can_be_reused: bool,
1874    }
1875
1876    let mut physical_images = Vec::<PhysicalImage>::default();
1877    let mut image_virtual_to_physical = FnvHashMap::<VirtualImageId, PhysicalImageId>::default();
1878    let mut physical_buffers = Vec::<PhysicalBuffer>::default();
1879    let mut buffer_virtual_to_physical = FnvHashMap::<VirtualBufferId, PhysicalBufferId>::default();
1880
1881    //
1882    // Allocate physical IDs for all input images/buffers
1883    //
1884    for external_image in &graph.external_images {
1885        if let Some(input_usage) = external_image.input_usage {
1886            let virtual_id = virtual_resources.image_usage_to_virtual[&input_usage];
1887            let physical_image = PhysicalImage {
1888                specification: external_image.specification.clone(),
1889                last_node_pass_index: passes.len() - 1,
1890                can_be_reused: false, // Should be safe to allow reuse? But last_node_pass_index effectively makes this never reuse
1891            };
1892
1893            let physical_image_id = PhysicalImageId(physical_images.len());
1894            physical_images.push(physical_image);
1895            let old = image_virtual_to_physical.insert(virtual_id, physical_image_id);
1896            assert!(old.is_none());
1897
1898            log::trace!(
1899                "  Input Image {:?} -> {:?} Used in passes [{}:{}]",
1900                virtual_id,
1901                physical_image_id,
1902                0,
1903                passes.len() - 1
1904            );
1905        }
1906    }
1907
1908    for external_buffer in &graph.external_buffers {
1909        if let Some(input_usage) = external_buffer.input_usage {
1910            let virtual_id = virtual_resources.buffer_usage_to_virtual[&input_usage];
1911            let physical_buffer = PhysicalBuffer {
1912                specification: external_buffer.specification.clone(),
1913                last_node_pass_index: passes.len() - 1,
1914                can_be_reused: false, // Should be safe to allow reuse? But last_node_pass_index effectively makes this never reuse
1915            };
1916
1917            let physical_buffer_id = PhysicalBufferId(physical_buffers.len());
1918            physical_buffers.push(physical_buffer);
1919            let old = buffer_virtual_to_physical.insert(virtual_id, physical_buffer_id);
1920            assert!(old.is_none());
1921
1922            log::trace!(
1923                "  Input Buffer {:?} -> {:?} Used in passes [{}:{}]",
1924                virtual_id,
1925                physical_buffer_id,
1926                0,
1927                passes.len() - 1
1928            );
1929        }
1930    }
1931
1932    //
1933    // Allocate physical IDs for all output images/buffers
1934    //
1935    for external_image in &graph.external_images {
1936        if let Some(output_usage) = external_image.output_usage {
1937            let virtual_id = virtual_resources.image_usage_to_virtual[&output_usage];
1938            let physical_image = PhysicalImage {
1939                specification: external_image.specification.clone(),
1940                last_node_pass_index: passes.len() - 1,
1941                can_be_reused: false, // Should be safe to allow reuse? But last_node_pass_index effectively makes this never reuse
1942            };
1943
1944            let physical_image_id = if let Some(existing_physical_image_id) =
1945                image_virtual_to_physical.get(&virtual_id)
1946            {
1947                // If an input image already exists, verify it created the same physical image entry
1948                let existing_physical_image = &physical_images[existing_physical_image_id.0];
1949                assert_eq!(physical_image, *existing_physical_image);
1950                *existing_physical_image_id
1951            } else {
1952                let physical_image_id = PhysicalImageId(physical_images.len());
1953                physical_images.push(physical_image);
1954                let old = image_virtual_to_physical.insert(virtual_id, physical_image_id);
1955                assert!(old.is_none());
1956                physical_image_id
1957            };
1958
1959            log::trace!(
1960                "  Output Image {:?} -> {:?} Used in passes [{}:{}]",
1961                virtual_id,
1962                physical_image_id,
1963                0,
1964                passes.len() - 1
1965            );
1966        }
1967    }
1968
1969    for external_buffer in &graph.external_buffers {
1970        if let Some(output_usage) = external_buffer.output_usage {
1971            let virtual_id = virtual_resources.buffer_usage_to_virtual[&output_usage];
1972            let physical_buffer = PhysicalBuffer {
1973                specification: external_buffer.specification.clone(),
1974                last_node_pass_index: passes.len() - 1,
1975                can_be_reused: false, // Should be safe to allow reuse? But last_node_pass_index effectively makes this never reuse
1976            };
1977
1978            let physical_buffer_id = if let Some(existing_physical_buffer_id) =
1979                buffer_virtual_to_physical.get(&virtual_id)
1980            {
1981                // If an input buffer already exists, verify it created the same physical buffer entry
1982                let existing_physical_buffer = &physical_buffers[existing_physical_buffer_id.0];
1983                assert_eq!(physical_buffer, *existing_physical_buffer);
1984                *existing_physical_buffer_id
1985            } else {
1986                let physical_buffer_id = PhysicalBufferId(physical_buffers.len());
1987                physical_buffers.push(physical_buffer);
1988                let old = buffer_virtual_to_physical.insert(virtual_id, physical_buffer_id);
1989                assert!(old.is_none());
1990                physical_buffer_id
1991            };
1992
1993            log::trace!(
1994                "  Output Buffer {:?} -> {:?} Used in passes [{}:{}]",
1995                virtual_id,
1996                physical_buffer_id,
1997                0,
1998                passes.len() - 1
1999            );
2000        }
2001    }
2002
2003    //
2004    // Determine the minimal set of physical images needed to represent all our virtual images,
2005    // given that virtual images can use the same physical image if their lifetimes don't overlap
2006    //
2007    // Images are sorted by first usage (because we register them in order of the passes that first
2008    // use them)
2009    //
2010    for reuse_requirements in &image_reuse_requirements {
2011        if image_virtual_to_physical.contains_key(&reuse_requirements.virtual_id) {
2012            // May already have been registered by output image
2013            continue;
2014        }
2015
2016        // See if we can reuse with an existing physical image
2017        let mut physical_image_id = None;
2018        for (physical_image_index, physical_image) in physical_images.iter_mut().enumerate() {
2019            if physical_image.last_node_pass_index < reuse_requirements.first_node_pass_index
2020                && physical_image.can_be_reused
2021            {
2022                if physical_image
2023                    .specification
2024                    .try_merge(&reuse_requirements.specification)
2025                {
2026                    physical_image.last_node_pass_index = reuse_requirements.last_node_pass_index;
2027                    physical_image_id = Some(PhysicalImageId(physical_image_index));
2028                    log::trace!(
2029                        "  Intermediate Image (Reuse) {:?} -> {:?} Used in passes [{}:{}]",
2030                        reuse_requirements.virtual_id,
2031                        physical_image_id,
2032                        reuse_requirements.first_node_pass_index,
2033                        reuse_requirements.last_node_pass_index
2034                    );
2035                    break;
2036                }
2037            }
2038        }
2039
2040        // If the existing physical images are not compatible, make a new one
2041        let physical_image_id = physical_image_id.unwrap_or_else(|| {
2042            let physical_image_id = PhysicalImageId(physical_images.len());
2043            physical_images.push(PhysicalImage {
2044                specification: reuse_requirements.specification.clone(),
2045                last_node_pass_index: reuse_requirements.last_node_pass_index,
2046                can_be_reused: true,
2047            });
2048
2049            log::trace!(
2050                "  Intermediate Image (Create new) {:?} -> {:?} Used in passes [{}:{}]",
2051                reuse_requirements.virtual_id,
2052                physical_image_id,
2053                reuse_requirements.first_node_pass_index,
2054                reuse_requirements.last_node_pass_index
2055            );
2056            physical_image_id
2057        });
2058
2059        image_virtual_to_physical.insert(reuse_requirements.virtual_id, physical_image_id);
2060    }
2061
2062    for reuse_requirements in &buffer_reuse_requirements {
2063        if buffer_virtual_to_physical.contains_key(&reuse_requirements.virtual_id) {
2064            // May already have been registered by output buffer
2065            continue;
2066        }
2067
2068        // See if we can reuse with an existing physical buffer
2069        let mut physical_buffer_id = None;
2070        for (physical_buffer_index, physical_buffer) in physical_buffers.iter_mut().enumerate() {
2071            if physical_buffer.last_node_pass_index < reuse_requirements.first_node_pass_index
2072                && physical_buffer.can_be_reused
2073            {
2074                if physical_buffer
2075                    .specification
2076                    .try_merge(&reuse_requirements.specification)
2077                {
2078                    physical_buffer.last_node_pass_index = reuse_requirements.last_node_pass_index;
2079                    physical_buffer_id = Some(PhysicalBufferId(physical_buffer_index));
2080                    log::trace!(
2081                        "  Intermediate Buffer (Reuse) {:?} -> {:?} Used in passes [{}:{}]",
2082                        reuse_requirements.virtual_id,
2083                        physical_buffer_id,
2084                        reuse_requirements.first_node_pass_index,
2085                        reuse_requirements.last_node_pass_index
2086                    );
2087                    break;
2088                }
2089            }
2090        }
2091
2092        // If the existing physical buffers are not compatible, make a new one
2093        let physical_buffer_id = physical_buffer_id.unwrap_or_else(|| {
2094            let physical_buffer_id = PhysicalBufferId(physical_buffers.len());
2095            physical_buffers.push(PhysicalBuffer {
2096                specification: reuse_requirements.specification.clone(),
2097                last_node_pass_index: reuse_requirements.last_node_pass_index,
2098                can_be_reused: true,
2099            });
2100
2101            log::trace!(
2102                "  Intermediate Buffer (Create new) {:?} -> {:?} Used in passes [{}:{}]",
2103                reuse_requirements.virtual_id,
2104                physical_buffer_id,
2105                reuse_requirements.first_node_pass_index,
2106                reuse_requirements.last_node_pass_index
2107            );
2108            physical_buffer_id
2109        });
2110
2111        buffer_virtual_to_physical.insert(reuse_requirements.virtual_id, physical_buffer_id);
2112    }
2113
2114    //
2115    // Create a lookup to get physical image from usage
2116    //
2117    let mut image_usage_to_physical = FnvHashMap::default();
2118    for (&usage, virtual_image) in &virtual_resources.image_usage_to_virtual {
2119        //TODO: This was breaking in a test because an output image had no usage flags and we
2120        // never assigned the output image a physical ID since it wasn't in a pass
2121        image_usage_to_physical.insert(usage, image_virtual_to_physical[virtual_image]);
2122    }
2123
2124    //
2125    // Create a lookup to get physical buffer from usage
2126    //
2127    let mut buffer_usage_to_physical = FnvHashMap::default();
2128    for (&usage, virtual_buffer) in &virtual_resources.buffer_usage_to_virtual {
2129        //TODO: This was breaking in a test because an output buffer had no usage flags and we
2130        // never assigned the output buffer a physical ID since it wasn't in a pass
2131        buffer_usage_to_physical.insert(usage, buffer_virtual_to_physical[virtual_buffer]);
2132    }
2133
2134    //
2135    // Setup image views
2136    //
2137
2138    // Temporary to build image view list/lookup
2139    let mut image_subresource_to_view = FnvHashMap::default();
2140
2141    // Create a list of all views needed for the graph and associating the usage with the view
2142    let mut image_views = Vec::default();
2143    let mut image_usage_to_image_view = FnvHashMap::default();
2144
2145    //
2146    // Create a list and lookup for all image views that are needed for the graph
2147    //
2148    for (&usage, &physical_image) in &image_usage_to_physical {
2149        let image_specification = constraints.image_specification(usage).unwrap();
2150        let image_view = RenderGraphImageView {
2151            physical_image,
2152            format: image_specification.format,
2153            view_options: graph.image_usage(usage).view_options.clone(),
2154        };
2155
2156        // Get the ID that matches the view, or insert a new view, generating a new ID
2157
2158        let image_view_id = *image_subresource_to_view
2159            .entry(image_view.clone())
2160            .or_insert_with(|| {
2161                let image_view_id = PhysicalImageViewId(image_views.len());
2162                image_views.push(image_view);
2163                image_view_id
2164            });
2165
2166        let old = image_usage_to_image_view.insert(usage, image_view_id);
2167        assert!(old.is_none());
2168    }
2169
2170    for pass in passes {
2171        if let RenderGraphPass::Render(renderpass) = pass {
2172            for attachment in &mut renderpass.attachments {
2173                let physical_image = image_virtual_to_physical[&attachment.virtual_image];
2174                let image_view_id = image_usage_to_image_view[&attachment.usage];
2175                attachment.image = Some(physical_image);
2176                attachment.image_view = Some(image_view_id);
2177            }
2178        }
2179    }
2180
2181    //
2182    // Create a list of all images that need to be created
2183    //
2184    let image_specifications: Vec<_> = physical_images
2185        .into_iter()
2186        .map(|x| x.specification)
2187        .collect();
2188
2189    //
2190    // Create a list of all buffers that need to be created
2191    //
2192    let buffer_specifications: Vec<_> = physical_buffers
2193        .into_iter()
2194        .map(|x| x.specification)
2195        .collect();
2196
2197    AssignPhysicalResourcesResult {
2198        image_usage_to_physical,
2199        image_virtual_to_physical,
2200        image_usage_to_image_view,
2201        image_views,
2202        image_specifications,
2203        buffer_usage_to_physical,
2204        buffer_virtual_to_physical,
2205        buffer_specifications,
2206    }
2207}
2208
2209fn add_image_barrier_for_node(
2210    physical_resources: &AssignPhysicalResourcesResult,
2211    image_node_barriers: &mut FnvHashMap<PhysicalImageId, RenderGraphPassImageBarriers>,
2212    image: RenderGraphImageUsageId,
2213    resource_state: RafxResourceState,
2214) {
2215    let physical_image = physical_resources
2216        .image_usage_to_physical
2217        .get(&image)
2218        .unwrap();
2219
2220    // If this assert fires, the image was used in multiple ways during the same pass
2221    let old = image_node_barriers.insert(
2222        *physical_image,
2223        RenderGraphPassImageBarriers::new(resource_state),
2224    );
2225    assert!(old.is_none());
2226}
2227
2228fn add_buffer_barrier_for_node(
2229    physical_resources: &AssignPhysicalResourcesResult,
2230    buffer_node_barriers: &mut FnvHashMap<PhysicalBufferId, RenderGraphPassBufferBarriers>,
2231    buffer: RenderGraphBufferUsageId,
2232    resource_state: RafxResourceState,
2233) {
2234    let physical_buffer = physical_resources
2235        .buffer_usage_to_physical
2236        .get(&buffer)
2237        .unwrap();
2238
2239    // If this assert fires, the image was used in multiple ways during the same pass
2240    let old = buffer_node_barriers.insert(
2241        *physical_buffer,
2242        RenderGraphPassBufferBarriers::new(resource_state),
2243    );
2244    assert!(old.is_none());
2245}
2246
2247#[profiling::function]
2248fn build_node_barriers(
2249    graph: &RenderGraphBuilder,
2250    node_execution_order: &[RenderGraphNodeId],
2251    _constraints: &DetermineConstraintsResult,
2252    physical_resources: &AssignPhysicalResourcesResult,
2253    builtin_final_node: RenderGraphNodeId,
2254) -> FnvHashMap<RenderGraphNodeId, RenderGraphNodeResourceBarriers> {
2255    let mut resource_barriers =
2256        FnvHashMap::<RenderGraphNodeId, RenderGraphNodeResourceBarriers>::default();
2257
2258    for node_id in node_execution_order {
2259        // A special final node is added to every graph to handle transitioning any output
2260        // images/buffers to their final state, so break out here and handle logic for this below
2261        if *node_id == builtin_final_node {
2262            break;
2263        }
2264
2265        let node = graph.node(*node_id);
2266        let mut image_node_barriers: FnvHashMap<PhysicalImageId, RenderGraphPassImageBarriers> =
2267            Default::default();
2268        let mut buffer_node_barriers: FnvHashMap<PhysicalBufferId, RenderGraphPassBufferBarriers> =
2269            Default::default();
2270
2271        //
2272        // RenderPass attachments
2273        //
2274        for color_attachment in &node.color_attachments {
2275            if let Some(color_attachment) = color_attachment {
2276                let read_or_write_usage = color_attachment
2277                    .read_image
2278                    .or(color_attachment.write_image)
2279                    .unwrap();
2280
2281                add_image_barrier_for_node(
2282                    physical_resources,
2283                    &mut image_node_barriers,
2284                    read_or_write_usage,
2285                    RafxResourceState::RENDER_TARGET,
2286                );
2287            }
2288        }
2289
2290        for resolve_attachment in &node.resolve_attachments {
2291            if let Some(resolve_attachment) = resolve_attachment {
2292                add_image_barrier_for_node(
2293                    physical_resources,
2294                    &mut image_node_barriers,
2295                    resolve_attachment.write_image,
2296                    RafxResourceState::RENDER_TARGET,
2297                );
2298            }
2299        }
2300
2301        if let Some(depth_attachment) = &node.depth_attachment {
2302            let read_or_write_usage = depth_attachment
2303                .read_image
2304                .or(depth_attachment.write_image)
2305                .unwrap();
2306
2307            add_image_barrier_for_node(
2308                physical_resources,
2309                &mut image_node_barriers,
2310                read_or_write_usage,
2311                RafxResourceState::DEPTH_WRITE,
2312            );
2313        }
2314
2315        //
2316        // Shader image resources
2317        //
2318        for &image in &node.sampled_images {
2319            add_image_barrier_for_node(
2320                physical_resources,
2321                &mut image_node_barriers,
2322                image,
2323                RafxResourceState::SHADER_RESOURCE,
2324            );
2325        }
2326
2327        for &image in &node.storage_image_creates {
2328            add_image_barrier_for_node(
2329                physical_resources,
2330                &mut image_node_barriers,
2331                image,
2332                RafxResourceState::UNORDERED_ACCESS,
2333            );
2334        }
2335
2336        for &image in &node.storage_image_reads {
2337            add_image_barrier_for_node(
2338                physical_resources,
2339                &mut image_node_barriers,
2340                image,
2341                RafxResourceState::UNORDERED_ACCESS,
2342            );
2343        }
2344
2345        for &image in &node.storage_image_modifies {
2346            add_image_barrier_for_node(
2347                physical_resources,
2348                &mut image_node_barriers,
2349                image,
2350                RafxResourceState::UNORDERED_ACCESS,
2351            );
2352        }
2353
2354        for &image in &node.copy_src_image_reads {
2355            add_image_barrier_for_node(
2356                physical_resources,
2357                &mut image_node_barriers,
2358                image,
2359                RafxResourceState::COPY_SRC,
2360            );
2361        }
2362
2363        for &image in &node.copy_dst_image_writes {
2364            add_image_barrier_for_node(
2365                physical_resources,
2366                &mut image_node_barriers,
2367                image,
2368                RafxResourceState::COPY_DST,
2369            );
2370        }
2371
2372        //
2373        // Shader buffer resources
2374        //
2375        for &buffer in &node.vertex_buffer_reads {
2376            add_buffer_barrier_for_node(
2377                physical_resources,
2378                &mut buffer_node_barriers,
2379                buffer,
2380                RafxResourceState::VERTEX_AND_CONSTANT_BUFFER,
2381            );
2382        }
2383
2384        for &buffer in &node.index_buffer_reads {
2385            add_buffer_barrier_for_node(
2386                physical_resources,
2387                &mut buffer_node_barriers,
2388                buffer,
2389                RafxResourceState::INDEX_BUFFER,
2390            );
2391        }
2392
2393        for &buffer in &node.indirect_buffer_reads {
2394            add_buffer_barrier_for_node(
2395                physical_resources,
2396                &mut buffer_node_barriers,
2397                buffer,
2398                RafxResourceState::INDIRECT_ARGUMENT,
2399            );
2400        }
2401
2402        for &buffer in &node.uniform_buffer_reads {
2403            add_buffer_barrier_for_node(
2404                physical_resources,
2405                &mut buffer_node_barriers,
2406                buffer,
2407                RafxResourceState::VERTEX_AND_CONSTANT_BUFFER,
2408            );
2409        }
2410
2411        for &buffer in &node.storage_buffer_creates {
2412            add_buffer_barrier_for_node(
2413                physical_resources,
2414                &mut buffer_node_barriers,
2415                buffer,
2416                RafxResourceState::UNORDERED_ACCESS,
2417            );
2418        }
2419
2420        for &buffer in &node.storage_buffer_reads {
2421            add_buffer_barrier_for_node(
2422                physical_resources,
2423                &mut buffer_node_barriers,
2424                buffer,
2425                RafxResourceState::UNORDERED_ACCESS,
2426            );
2427        }
2428
2429        for &buffer in &node.storage_buffer_modifies {
2430            add_buffer_barrier_for_node(
2431                physical_resources,
2432                &mut buffer_node_barriers,
2433                buffer,
2434                RafxResourceState::UNORDERED_ACCESS,
2435            );
2436        }
2437
2438        for &buffer in &node.copy_src_buffer_reads {
2439            add_buffer_barrier_for_node(
2440                physical_resources,
2441                &mut buffer_node_barriers,
2442                buffer,
2443                RafxResourceState::COPY_SRC,
2444            );
2445        }
2446
2447        for &buffer in &node.copy_dst_buffer_writes {
2448            add_buffer_barrier_for_node(
2449                physical_resources,
2450                &mut buffer_node_barriers,
2451                buffer,
2452                RafxResourceState::COPY_DST,
2453            );
2454        }
2455
2456        resource_barriers.insert(
2457            *node_id,
2458            RenderGraphNodeResourceBarriers {
2459                image_barriers: image_node_barriers,
2460                buffer_barriers: buffer_node_barriers,
2461            },
2462        );
2463    }
2464
2465    // A special final node is added to every graph to handle transitioning any output
2466    // images/buffers to their final state. We handle setting up the required barriers for that
2467    // node here.
2468    let _builtin_final_node = graph.node(builtin_final_node);
2469    let mut final_image_node_barriers: FnvHashMap<PhysicalImageId, RenderGraphPassImageBarriers> =
2470        Default::default();
2471    let mut final_buffer_node_barriers: FnvHashMap<
2472        PhysicalBufferId,
2473        RenderGraphPassBufferBarriers,
2474    > = Default::default();
2475
2476    for external_image in &graph.external_images {
2477        if let Some(output_usage) = external_image.output_usage {
2478            add_image_barrier_for_node(
2479                physical_resources,
2480                &mut final_image_node_barriers,
2481                output_usage,
2482                external_image.final_state,
2483            );
2484        }
2485    }
2486
2487    for external_buffer in &graph.external_buffers {
2488        if let Some(output_usage) = external_buffer.output_usage {
2489            add_buffer_barrier_for_node(
2490                physical_resources,
2491                &mut final_buffer_node_barriers,
2492                output_usage,
2493                external_buffer.final_state,
2494            );
2495        }
2496    }
2497
2498    resource_barriers.insert(
2499        builtin_final_node,
2500        RenderGraphNodeResourceBarriers {
2501            image_barriers: final_image_node_barriers,
2502            buffer_barriers: final_buffer_node_barriers,
2503        },
2504    );
2505
2506    resource_barriers
2507}
2508
2509// * At this point we know images/image views, format, samples, load/store ops. We also know what
2510//   needs to be flushed/invalidated
2511// * We want to determine layouts and the validates/flushes we actually need to insert. Essentially
2512//   we simulate executing the graph in sequence and keep up with what's been invalidated/flushed,
2513//   and what layouts images are in when the respective node is run.
2514#[profiling::function]
2515fn build_pass_barriers(
2516    graph: &RenderGraphBuilder,
2517    _node_execution_order: &[RenderGraphNodeId],
2518    _constraints: &DetermineConstraintsResult,
2519    physical_resources: &AssignPhysicalResourcesResult,
2520    node_barriers: &FnvHashMap<RenderGraphNodeId, RenderGraphNodeResourceBarriers>,
2521    passes: &mut [RenderGraphPass],
2522) {
2523    log::trace!("-- build_pass_barriers --");
2524
2525    //
2526    // We will walk through all nodes keeping track of memory access as we go
2527    //
2528    struct ImageState {
2529        resource_state: RafxResourceState,
2530    }
2531
2532    impl Default for ImageState {
2533        fn default() -> Self {
2534            ImageState {
2535                resource_state: RafxResourceState::UNDEFINED,
2536            }
2537        }
2538    }
2539
2540    struct BufferState {
2541        resource_state: RafxResourceState,
2542    }
2543
2544    impl Default for BufferState {
2545        fn default() -> Self {
2546            BufferState {
2547                //DX12TODO: This was UNDEFINED but DX12 seems to need it to be COPY_DST?
2548                resource_state: RafxResourceState::COPY_DST,
2549            }
2550        }
2551    }
2552
2553    //TODO: Starting from UNDEFINED initial state is generally bad, we are reusing resources, we
2554    // could know what state it was in
2555
2556    //TODO: to support subpass, probably need image states for each previous subpass
2557    // TODO: This is coarse-grained over the whole image. Ideally it would be per-layer and per-mip
2558    let mut image_states: Vec<ImageState> =
2559        Vec::with_capacity(physical_resources.image_specifications.len());
2560    image_states.resize_with(physical_resources.image_specifications.len(), || {
2561        Default::default()
2562    });
2563
2564    let mut buffer_states: Vec<BufferState> =
2565        Vec::with_capacity(physical_resources.buffer_specifications.len());
2566    buffer_states.resize_with(physical_resources.buffer_specifications.len(), || {
2567        Default::default()
2568    });
2569
2570    // Populate init state for external images/buffers
2571    for external_image in &graph.external_images {
2572        if let Some(input_usage) = external_image.input_usage {
2573            let physical_id = physical_resources.image_usage_to_physical[&input_usage];
2574            image_states[physical_id.0].resource_state = external_image.initial_state;
2575        }
2576    }
2577
2578    for external_buffer in &graph.external_buffers {
2579        if let Some(input_usage) = external_buffer.input_usage {
2580            let physical_id = physical_resources.buffer_usage_to_physical[&input_usage];
2581            buffer_states[physical_id.0].resource_state = external_buffer.initial_state;
2582        }
2583    }
2584
2585    for (pass_index, pass) in passes.iter_mut().enumerate() {
2586        log::trace!("pass {}", pass_index);
2587
2588        // Initial layout for all attachments at the start of the renderpass
2589        let mut attachment_initial_state: Vec<Option<RafxResourceState>> = Default::default();
2590        if let RenderGraphPass::Render(pass) = pass {
2591            attachment_initial_state.resize_with(pass.attachments.len(), || None);
2592        }
2593
2594        //let nodes: Vec<_> = pass.nodes().iter().copied().collect();
2595        //for (subpass_index, subpass_node_id) in nodes.iter().enumerate() {
2596        let subpass_node_id = pass.node();
2597        let node_barriers = &node_barriers[&subpass_node_id];
2598
2599        struct ImageTransition {
2600            physical_image_id: PhysicalImageId,
2601            old_state: RafxResourceState,
2602            new_state: RafxResourceState,
2603        }
2604
2605        struct BufferTransition {
2606            physical_buffer_id: PhysicalBufferId,
2607            old_state: RafxResourceState,
2608            new_state: RafxResourceState,
2609        }
2610
2611        let mut image_transitions = Vec::default();
2612        // Look at all the images we read and determine what invalidates we need
2613        for (physical_image_id, image_barrier) in &node_barriers.image_barriers {
2614            log::trace!("    image {:?}", physical_image_id);
2615            let image_state = &mut image_states[physical_image_id.0];
2616
2617            let resource_state_change = image_state.resource_state != image_barrier.resource_state;
2618            if resource_state_change {
2619                log::trace!(
2620                    "      state change! {:?} -> {:?}",
2621                    image_state.resource_state,
2622                    image_barrier.resource_state
2623                );
2624
2625                if resource_state_change {
2626                    image_transitions.push(ImageTransition {
2627                        physical_image_id: *physical_image_id,
2628                        old_state: image_state.resource_state,
2629                        new_state: image_barrier.resource_state,
2630                    });
2631                }
2632
2633                image_state.resource_state = image_barrier.resource_state;
2634            }
2635
2636            // Set the initial layout for the attachment, but only if it's the first time we've seen it
2637            //TODO: This is bad and does not properly handle an image being used in multiple ways requiring
2638            // multiple layouts
2639            if let RenderGraphPass::Render(pass) = pass {
2640                for (attachment_index, attachment) in &mut pass.attachments.iter_mut().enumerate() {
2641                    //log::trace!("      attachment {:?}", attachment.image);
2642                    if attachment.image.unwrap() == *physical_image_id {
2643                        if attachment_initial_state[attachment_index].is_none() {
2644                            //log::trace!("        initial layout {:?}", image_barrier.layout);
2645                            attachment_initial_state[attachment_index] =
2646                                Some(image_state.resource_state.into());
2647
2648                            // Use an image barrier before the pass to transition the layout,
2649                            // so we will already be in the correct layout before starting the
2650                            // pass.
2651                            attachment.initial_state = image_barrier.resource_state.into();
2652                        }
2653
2654                        attachment.final_state = image_barrier.resource_state.into();
2655                        break;
2656                    }
2657                }
2658            }
2659        }
2660
2661        // Look at all the buffers we read and determine what invalidates we need
2662        let mut buffer_transitions = Vec::default();
2663        for (physical_buffer_id, buffer_barrier) in &node_barriers.buffer_barriers {
2664            log::trace!("    buffer {:?}", physical_buffer_id);
2665            let buffer_state = &mut buffer_states[physical_buffer_id.0];
2666
2667            let resource_state_change =
2668                buffer_state.resource_state != buffer_barrier.resource_state;
2669            if resource_state_change {
2670                log::trace!(
2671                    "      state change! {:?} -> {:?}",
2672                    buffer_state.resource_state,
2673                    buffer_barrier.resource_state
2674                );
2675
2676                buffer_transitions.push(BufferTransition {
2677                    physical_buffer_id: *physical_buffer_id,
2678                    old_state: buffer_state.resource_state,
2679                    new_state: buffer_barrier.resource_state,
2680                });
2681
2682                buffer_state.resource_state = buffer_barrier.resource_state;
2683            }
2684        }
2685
2686        let image_barriers: Vec<_> = image_transitions
2687            .into_iter()
2688            .map(|image_transition| {
2689                assert_ne!(image_transition.new_state, RafxResourceState::UNDEFINED);
2690                PrepassImageBarrier {
2691                    image: image_transition.physical_image_id,
2692                    old_state: image_transition.old_state,
2693                    new_state: image_transition.new_state,
2694                }
2695            })
2696            .collect();
2697
2698        let buffer_barriers: Vec<_> = buffer_transitions
2699            .into_iter()
2700            .map(|buffer_transition| {
2701                assert_ne!(buffer_transition.new_state, RafxResourceState::UNDEFINED);
2702                PrepassBufferBarrier {
2703                    buffer: buffer_transition.physical_buffer_id,
2704                    old_state: buffer_transition.old_state,
2705                    new_state: buffer_transition.new_state,
2706                }
2707            })
2708            .collect();
2709
2710        if !image_barriers.is_empty() || !buffer_barriers.is_empty() {
2711            let barrier = PrepassBarrier {
2712                image_barriers,
2713                buffer_barriers,
2714            };
2715
2716            pass.set_pre_pass_barrier(barrier);
2717        }
2718    }
2719}
2720
2721#[profiling::function]
2722fn create_output_passes(
2723    graph: &RenderGraphBuilder,
2724    passes: Vec<RenderGraphPass>,
2725) -> Vec<RenderGraphOutputPass> {
2726    let mut renderpasses = Vec::with_capacity(passes.len());
2727
2728    for pass in passes {
2729        let render_node = graph.node(pass.node());
2730        let debug_name = render_node.name;
2731
2732        match pass {
2733            RenderGraphPass::Render(pass) => {
2734                let attachment_images = pass
2735                    .attachments
2736                    .iter()
2737                    .map(|attachment| attachment.image_view.unwrap())
2738                    .collect();
2739
2740                let mut color_formats = vec![];
2741                let mut sample_count = None;
2742                for color_attachment in &pass.color_attachments {
2743                    if let Some(color_attachment) = color_attachment {
2744                        color_formats.push(pass.attachments[*color_attachment].format);
2745
2746                        let expected_sample_count = pass.attachments[*color_attachment].samples;
2747                        if let Some(sample_count) = sample_count {
2748                            assert_eq!(sample_count, expected_sample_count, "Render node has color attachments with different sample counts, this is unsupported.");
2749                        } else {
2750                            sample_count = Some(expected_sample_count);
2751                        }
2752                    }
2753                }
2754
2755                let mut depth_format = None;
2756                if let Some(depth_attachment) = pass.depth_attachment {
2757                    depth_format = Some(pass.attachments[depth_attachment].format);
2758
2759                    let expected_sample_count = pass.attachments[depth_attachment].samples;
2760                    if let Some(sample_count) = sample_count {
2761                        assert_eq!(sample_count, expected_sample_count, "Render node has color attachment and depth attachment with different sample counts, this is unsupported.");
2762                    } else {
2763                        sample_count = Some(expected_sample_count);
2764                    }
2765                }
2766
2767                let render_target_meta = GraphicsPipelineRenderTargetMeta::new(
2768                    color_formats,
2769                    depth_format,
2770                    sample_count.unwrap(),
2771                );
2772
2773                let mut color_render_targets = Vec::with_capacity(MAX_COLOR_ATTACHMENTS);
2774
2775                for (color_index, attachment_index) in pass.color_attachments.iter().enumerate() {
2776                    if let Some(attachment_index) = attachment_index {
2777                        let attachment = &pass.attachments[*attachment_index]; //.image.unwrap();
2778                        let attachment_usage = &graph.image_usages[attachment.usage.0];
2779                        let array_slice = attachment_usage.view_options.array_slice;
2780                        let mip_slice = attachment_usage.view_options.mip_slice;
2781
2782                        let mut resolve_image = None;
2783                        let mut resolve_array_slice = None;
2784                        let mut resolve_mip_slice = None;
2785                        let mut resolve_store_op = RafxStoreOp::DontCare;
2786                        if let Some(resolve_attachment_index) =
2787                            pass.resolve_attachments[color_index]
2788                        {
2789                            let resolve_attachment = &pass.attachments[resolve_attachment_index]; //.image.unwrap();
2790                            let resolve_attachment_usage =
2791                                &graph.image_usages[resolve_attachment.usage.0];
2792                            resolve_image = Some(resolve_attachment.image.unwrap());
2793                            resolve_array_slice = resolve_attachment_usage.view_options.array_slice;
2794                            resolve_mip_slice = resolve_attachment_usage.view_options.mip_slice;
2795                            resolve_store_op = resolve_attachment.store_op;
2796                        }
2797
2798                        color_render_targets.push(RenderGraphColorRenderTarget {
2799                            image: attachment.image.unwrap(),
2800                            load_op: attachment.load_op,
2801                            store_op: attachment.store_op,
2802                            clear_value: attachment
2803                                .clear_color
2804                                .clone()
2805                                .map(|x| x.to_color_clear_value())
2806                                .unwrap_or_default(),
2807                            array_slice,
2808                            mip_slice,
2809                            resolve_image,
2810                            resolve_store_op,
2811                            resolve_array_slice,
2812                            resolve_mip_slice,
2813                        });
2814                    }
2815                }
2816
2817                let mut depth_stencil_render_target = None;
2818                if let Some(attachment_index) = pass.depth_attachment {
2819                    let attachment = &pass.attachments[attachment_index];
2820                    let array_slice = graph.image_usages[attachment.usage.0]
2821                        .view_options
2822                        .array_slice;
2823                    let mip_slice = graph.image_usages[attachment.usage.0]
2824                        .view_options
2825                        .mip_slice;
2826                    depth_stencil_render_target = Some(RenderGraphDepthStencilRenderTarget {
2827                        image: attachment.image.unwrap(),
2828                        depth_load_op: attachment.load_op,
2829                        stencil_load_op: attachment.stencil_load_op,
2830                        depth_store_op: attachment.store_op,
2831                        stencil_store_op: attachment.stencil_store_op,
2832                        clear_value: attachment
2833                            .clear_color
2834                            .clone()
2835                            .map(|x| x.to_depth_stencil_clear_value())
2836                            .unwrap_or_default(),
2837                        array_slice,
2838                        mip_slice,
2839                    });
2840                }
2841
2842                let output_pass = RenderGraphOutputRenderPass {
2843                    node_id: pass.node_id,
2844                    attachment_images,
2845                    pre_pass_barrier: pass.pre_pass_barrier,
2846                    debug_name,
2847                    color_render_targets,
2848                    depth_stencil_render_target,
2849                    render_target_meta,
2850                };
2851
2852                renderpasses.push(RenderGraphOutputPass::Render(output_pass));
2853            }
2854            RenderGraphPass::Callback(pass) => {
2855                let output_pass = RenderGraphOutputCallbackPass {
2856                    node: pass.node,
2857                    pre_pass_barrier: pass.pre_pass_barrier,
2858                    debug_name,
2859                };
2860
2861                renderpasses.push(RenderGraphOutputPass::Callback(output_pass));
2862            }
2863        }
2864    }
2865
2866    renderpasses
2867}
2868
2869#[allow(dead_code)]
2870fn print_constraints(
2871    graph: &RenderGraphBuilder,
2872    constraint_results: &mut DetermineConstraintsResult,
2873) {
2874    log::trace!("Image constraints:");
2875    for (image_index, image_resource) in graph.image_resources.iter().enumerate() {
2876        log::trace!("  Image {:?} {:?}", image_index, image_resource.name);
2877        for (version_index, version) in image_resource.versions.iter().enumerate() {
2878            log::trace!("    Version {}", version_index);
2879
2880            log::trace!(
2881                "      Writen as: {:?}",
2882                constraint_results.image_specification(version.create_usage)
2883            );
2884
2885            for (usage_index, usage) in version.read_usages.iter().enumerate() {
2886                log::trace!(
2887                    "      Read Usage {}: {:?}",
2888                    usage_index,
2889                    constraint_results.image_specification(*usage)
2890                );
2891            }
2892        }
2893    }
2894
2895    log::trace!("Buffer constraints:");
2896    for (buffer_index, buffer_resource) in graph.buffer_resources.iter().enumerate() {
2897        log::trace!("  Buffer {:?} {:?}", buffer_index, buffer_resource.name);
2898        for (version_index, version) in buffer_resource.versions.iter().enumerate() {
2899            log::trace!("    Version {}", version_index);
2900
2901            log::trace!(
2902                "      Writen as: {:?}",
2903                constraint_results.buffer_specification(version.create_usage)
2904            );
2905
2906            for (usage_index, usage) in version.read_usages.iter().enumerate() {
2907                log::trace!(
2908                    "      Read Usage {}: {:?}",
2909                    usage_index,
2910                    constraint_results.buffer_specification(*usage)
2911                );
2912            }
2913        }
2914    }
2915}
2916
2917#[allow(dead_code)]
2918fn print_image_compatibility(
2919    graph: &RenderGraphBuilder,
2920    constraint_results: &DetermineConstraintsResult,
2921) {
2922    log::trace!("Image Compatibility Report:");
2923    for (image_index, image_resource) in graph.image_resources.iter().enumerate() {
2924        log::trace!("  Image {:?} {:?}", image_index, image_resource.name);
2925        for (version_index, version) in image_resource.versions.iter().enumerate() {
2926            let write_specification = constraint_results.image_specification(version.create_usage);
2927
2928            log::trace!("    Version {}: {:?}", version_index, version);
2929            for (usage_index, usage) in version.read_usages.iter().enumerate() {
2930                let read_specification = constraint_results.image_specification(*usage);
2931
2932                // TODO: Skip images we don't use?
2933
2934                if write_specification == read_specification {
2935                    log::trace!("      read usage {} matches", usage_index);
2936                } else {
2937                    log::trace!("      read usage {} does not match", usage_index);
2938                    log::trace!("        produced: {:?}", write_specification);
2939                    log::trace!("        required: {:?}", read_specification);
2940                }
2941            }
2942        }
2943    }
2944}
2945
2946#[allow(dead_code)]
2947fn print_node_barriers(
2948    node_barriers: &FnvHashMap<RenderGraphNodeId, RenderGraphNodeResourceBarriers>
2949) {
2950    log::trace!("Barriers:");
2951    for (node_id, barriers) in node_barriers.iter() {
2952        log::trace!("  pass {:?}", node_id);
2953        log::trace!("    resource states");
2954        for (physical_id, barriers) in &barriers.image_barriers {
2955            log::trace!("      {:?}: {:?}", physical_id, barriers.resource_state);
2956        }
2957
2958        for (physical_id, barriers) in &barriers.buffer_barriers {
2959            log::trace!("      {:?}: {:?}", physical_id, barriers.resource_state);
2960        }
2961    }
2962}
2963
2964#[allow(dead_code)]
2965fn verify_unculled_image_usages_specifications_exist(
2966    graph: &RenderGraphBuilder,
2967    node_execution_order: &Vec<RenderGraphNodeId>,
2968    constraint_results: &DetermineConstraintsResult,
2969) {
2970    for (_image_index, image_resource) in graph.image_resources.iter().enumerate() {
2971        //log::trace!("  Image {:?} {:?}", image_index, image_resource.name);
2972        for (_version_index, version) in image_resource.versions.iter().enumerate() {
2973            // Check the write usage for this version
2974            if node_execution_order.contains(&version.creator_node)
2975                && constraint_results
2976                    .images
2977                    .get(&version.create_usage)
2978                    .is_none()
2979            {
2980                let usage_info = &graph.image_usages[version.create_usage.0];
2981                panic!(
2982                    "Could not determine specification for image {:?} use by {:?} for {:?}",
2983                    version.create_usage, usage_info.user, usage_info.usage_type
2984                );
2985            }
2986
2987            // Check the read usages for this version
2988            for (_, usage) in version.read_usages.iter().enumerate() {
2989                let usage_info = &graph.image_usages[usage.0];
2990                let is_scheduled = match &usage_info.user {
2991                    RenderGraphImageUser::Node(node_id) => node_execution_order.contains(node_id),
2992                    RenderGraphImageUser::Input(_) => true,
2993                    RenderGraphImageUser::Output(_) => true,
2994                };
2995
2996                if is_scheduled && constraint_results.images.get(usage).is_none() {
2997                    panic!(
2998                        "Could not determine specification for image {:?} used by {:?} for {:?}",
2999                        usage, usage_info.user, usage_info.usage_type
3000                    );
3001                }
3002            }
3003        }
3004    }
3005}
3006
3007#[allow(dead_code)]
3008fn print_final_images(
3009    assign_physical_resources_result: &AssignPhysicalResourcesResult,
3010    external_images: &FnvHashMap<PhysicalImageViewId, RenderGraphPlanExternalImage>,
3011    intermediate_images: &FnvHashMap<PhysicalImageId, RenderGraphImageSpecification>,
3012) {
3013    log::trace!("-- IMAGES --");
3014    for (physical_id, intermediate_image_spec) in intermediate_images {
3015        log::trace!(
3016            "Intermediate Image: {:?} {:?}",
3017            physical_id,
3018            intermediate_image_spec
3019        );
3020    }
3021
3022    for (physical_id, external_image) in external_images {
3023        log::trace!(
3024            "External Image: {:?} {:?}",
3025            assign_physical_resources_result.image_views[physical_id.0].physical_image,
3026            external_image
3027        );
3028    }
3029}
3030
3031#[allow(dead_code)]
3032fn print_final_buffers(
3033    external_buffers: &FnvHashMap<PhysicalBufferId, RenderGraphPlanExternalBuffer>,
3034    intermediate_buffers: &FnvHashMap<PhysicalBufferId, RenderGraphBufferSpecification>,
3035) {
3036    log::trace!("-- BUFFERS --");
3037    for (physical_id, intermediate_image_spec) in intermediate_buffers {
3038        log::trace!(
3039            "Intermediate Buffer: {:?} {:?}",
3040            physical_id,
3041            intermediate_image_spec
3042        );
3043    }
3044    for (physical_id, external_image) in external_buffers {
3045        log::trace!("External Buffer: {:?} {:?}", physical_id, external_image);
3046    }
3047}
3048
3049#[allow(dead_code)]
3050fn print_final_resource_usages(
3051    graph: &RenderGraphBuilder,
3052    assign_physical_resources_result: &AssignPhysicalResourcesResult,
3053    constraint_results: &DetermineConstraintsResult,
3054    renderpasses: &Vec<RenderGraphOutputPass>,
3055) {
3056    fn print_image_resource_usage(
3057        graph: &RenderGraphBuilder,
3058        assign_physical_resources_result: &AssignPhysicalResourcesResult,
3059        constraint_results: &DetermineConstraintsResult,
3060        image: RenderGraphImageUsageId,
3061        prefix: &str,
3062    ) {
3063        let physical_image = assign_physical_resources_result.image_usage_to_physical[&image];
3064        let write_name = graph.image_resource(image).name;
3065        log::debug!(
3066            "    {}: {:?} Name: {:?} Constraints: {:?}",
3067            prefix,
3068            physical_image,
3069            write_name,
3070            constraint_results.images[&image]
3071        );
3072    }
3073
3074    fn print_buffer_resource_usage(
3075        graph: &RenderGraphBuilder,
3076        assign_physical_resources_result: &AssignPhysicalResourcesResult,
3077        constraint_results: &DetermineConstraintsResult,
3078        buffer: RenderGraphBufferUsageId,
3079        prefix: &str,
3080    ) {
3081        let physical_buffer = assign_physical_resources_result.buffer_usage_to_physical[&buffer];
3082        let write_name = graph.buffer_resource(buffer).name;
3083        log::debug!(
3084            "    {}: {:?} Name: {:?} Constraints: {:?}",
3085            prefix,
3086            physical_buffer,
3087            write_name,
3088            constraint_results.buffers[&buffer]
3089        );
3090    }
3091
3092    log::debug!("-- RESOURCE USAGE --");
3093    for (pass_index, pass) in renderpasses.iter().enumerate() {
3094        log::debug!("pass {}", pass_index);
3095
3096        let node = graph.node(pass.node());
3097        log::debug!("  subpass {:?} {:?}", pass.node(), node.name);
3098
3099        for (color_attachment_index, color_attachment) in node.color_attachments.iter().enumerate()
3100        {
3101            if let Some(color_attachment) = color_attachment {
3102                let read_or_write = color_attachment
3103                    .read_image
3104                    .or_else(|| color_attachment.write_image)
3105                    .unwrap();
3106                print_image_resource_usage(
3107                    graph,
3108                    assign_physical_resources_result,
3109                    constraint_results,
3110                    read_or_write,
3111                    &format!("Color Attachment {}", color_attachment_index),
3112                );
3113            }
3114        }
3115
3116        for (resolve_attachment_index, resolve_attachment) in
3117            node.resolve_attachments.iter().enumerate()
3118        {
3119            if let Some(resolve_attachment) = resolve_attachment {
3120                print_image_resource_usage(
3121                    graph,
3122                    assign_physical_resources_result,
3123                    constraint_results,
3124                    resolve_attachment.write_image,
3125                    &format!("Resolve Attachment {}", resolve_attachment_index),
3126                );
3127            }
3128        }
3129
3130        if let Some(depth_attachment) = &node.depth_attachment {
3131            let read_or_write = depth_attachment
3132                .read_image
3133                .or_else(|| depth_attachment.write_image)
3134                .unwrap();
3135            print_image_resource_usage(
3136                graph,
3137                assign_physical_resources_result,
3138                constraint_results,
3139                read_or_write,
3140                "Depth Attachment",
3141            );
3142        }
3143
3144        for &image in &node.sampled_images {
3145            print_image_resource_usage(
3146                graph,
3147                assign_physical_resources_result,
3148                constraint_results,
3149                image,
3150                "Sampled",
3151            );
3152        }
3153
3154        for &image in &node.storage_image_creates {
3155            print_image_resource_usage(
3156                graph,
3157                assign_physical_resources_result,
3158                constraint_results,
3159                image,
3160                "Storage Image (Create)",
3161            );
3162        }
3163
3164        for &image in &node.storage_image_reads {
3165            print_image_resource_usage(
3166                graph,
3167                assign_physical_resources_result,
3168                constraint_results,
3169                image,
3170                "Storage Image (Read)",
3171            );
3172        }
3173
3174        for &image in &node.storage_image_modifies {
3175            print_image_resource_usage(
3176                graph,
3177                assign_physical_resources_result,
3178                constraint_results,
3179                image,
3180                "Storage Image (Modify)",
3181            );
3182        }
3183
3184        for &image in &node.copy_src_image_reads {
3185            print_image_resource_usage(
3186                graph,
3187                assign_physical_resources_result,
3188                constraint_results,
3189                image,
3190                "Copy Src Image",
3191            );
3192        }
3193
3194        for &image in &node.copy_dst_image_writes {
3195            print_image_resource_usage(
3196                graph,
3197                assign_physical_resources_result,
3198                constraint_results,
3199                image,
3200                "Copy Dst Image",
3201            );
3202        }
3203
3204        for &buffer in &node.vertex_buffer_reads {
3205            print_buffer_resource_usage(
3206                graph,
3207                assign_physical_resources_result,
3208                constraint_results,
3209                buffer,
3210                "Vertex Buffer",
3211            );
3212        }
3213
3214        for &buffer in &node.index_buffer_reads {
3215            print_buffer_resource_usage(
3216                graph,
3217                assign_physical_resources_result,
3218                constraint_results,
3219                buffer,
3220                "Index Buffer",
3221            );
3222        }
3223
3224        for &buffer in &node.indirect_buffer_reads {
3225            print_buffer_resource_usage(
3226                graph,
3227                assign_physical_resources_result,
3228                constraint_results,
3229                buffer,
3230                "Indirect Buffer",
3231            );
3232        }
3233
3234        for &buffer in &node.uniform_buffer_reads {
3235            print_buffer_resource_usage(
3236                graph,
3237                assign_physical_resources_result,
3238                constraint_results,
3239                buffer,
3240                "Uniform Buffer",
3241            );
3242        }
3243
3244        for &buffer in &node.storage_buffer_creates {
3245            print_buffer_resource_usage(
3246                graph,
3247                assign_physical_resources_result,
3248                constraint_results,
3249                buffer,
3250                "Storage Buffer (Create)",
3251            );
3252        }
3253
3254        for &buffer in &node.storage_buffer_reads {
3255            print_buffer_resource_usage(
3256                graph,
3257                assign_physical_resources_result,
3258                constraint_results,
3259                buffer,
3260                "Storage Buffer (Read)",
3261            );
3262        }
3263
3264        for &buffer in &node.storage_buffer_modifies {
3265            print_buffer_resource_usage(
3266                graph,
3267                assign_physical_resources_result,
3268                constraint_results,
3269                buffer,
3270                "Storage Buffer (Modify)",
3271            );
3272        }
3273
3274        for &buffer in &node.copy_src_buffer_reads {
3275            print_buffer_resource_usage(
3276                graph,
3277                assign_physical_resources_result,
3278                constraint_results,
3279                buffer,
3280                "Copy Src Buffer",
3281            );
3282        }
3283
3284        for &buffer in &node.copy_dst_buffer_writes {
3285            print_buffer_resource_usage(
3286                graph,
3287                assign_physical_resources_result,
3288                constraint_results,
3289                buffer,
3290                "Copy Dst Buffer",
3291            );
3292        }
3293    }
3294
3295    log::debug!("External Resources");
3296
3297    for external_image in &graph.external_images {
3298        if let Some(input_usage) = external_image.input_usage {
3299            print_image_resource_usage(
3300                graph,
3301                assign_physical_resources_result,
3302                constraint_results,
3303                input_usage,
3304                "Read External Image",
3305            );
3306        }
3307
3308        if let Some(output_usage) = external_image.output_usage {
3309            print_image_resource_usage(
3310                graph,
3311                assign_physical_resources_result,
3312                constraint_results,
3313                output_usage,
3314                "Write External Image",
3315            );
3316        }
3317    }
3318
3319    for external_buffer in &graph.external_buffers {
3320        if let Some(input_usage) = external_buffer.input_usage {
3321            print_buffer_resource_usage(
3322                graph,
3323                assign_physical_resources_result,
3324                constraint_results,
3325                input_usage,
3326                "Read External Buffer",
3327            );
3328        }
3329
3330        if let Some(output_usage) = external_buffer.output_usage {
3331            print_buffer_resource_usage(
3332                graph,
3333                assign_physical_resources_result,
3334                constraint_results,
3335                output_usage,
3336                "Write External Buffer",
3337            );
3338        }
3339    }
3340}
3341
3342#[derive(Debug)]
3343pub struct RenderGraphPlanExternalImage {
3344    pub id: RenderGraphExternalImageId,
3345    pub resource: ResourceArc<ImageViewResource>,
3346}
3347
3348#[derive(Debug)]
3349pub struct RenderGraphPlanExternalBuffer {
3350    pub id: RenderGraphExternalBufferId,
3351    pub resource: ResourceArc<BufferResource>,
3352}
3353
3354/// The final output of a render graph, which will be consumed by PreparedRenderGraph. This just
3355/// includes the computed metadata and does not allocate resources.
3356pub struct RenderGraphPlan {
3357    pub(super) passes: Vec<RenderGraphOutputPass>,
3358    pub(super) external_images: FnvHashMap<PhysicalImageViewId, RenderGraphPlanExternalImage>,
3359    pub(super) external_buffers: FnvHashMap<PhysicalBufferId, RenderGraphPlanExternalBuffer>,
3360    pub(super) intermediate_images: FnvHashMap<PhysicalImageId, RenderGraphImageSpecification>,
3361    pub(super) intermediate_buffers: FnvHashMap<PhysicalBufferId, RenderGraphBufferSpecification>,
3362    pub(super) image_views: Vec<RenderGraphImageView>, // index by physical image view id
3363    pub(super) node_to_pass_index: FnvHashMap<RenderGraphNodeId, usize>,
3364    pub(super) _image_usage_to_physical: FnvHashMap<RenderGraphImageUsageId, PhysicalImageId>,
3365    pub(super) image_usage_to_view: FnvHashMap<RenderGraphImageUsageId, PhysicalImageViewId>,
3366    pub(super) buffer_usage_to_physical: FnvHashMap<RenderGraphBufferUsageId, PhysicalBufferId>,
3367
3368    // callbacks
3369    pub(super) visit_node_callbacks:
3370        FnvHashMap<RenderGraphNodeId, RenderGraphNodeVisitNodeCallback>,
3371    pub(super) _render_phase_dependencies:
3372        FnvHashMap<RenderGraphNodeId, FnvHashSet<RenderPhaseIndex>>,
3373}
3374
3375impl RenderGraphPlan {
3376    #[profiling::function]
3377    pub(super) fn new(
3378        mut graph: RenderGraphBuilder,
3379        swapchain_surface_info: &SwapchainSurfaceInfo,
3380    ) -> RenderGraphPlan {
3381        log::trace!("-- Create render graph plan --");
3382
3383        //
3384        // We add an extra node that always runs at the end. This lets us use the same codepath for
3385        // inserting barriers before nodes in the graph to insert barriers at the end of the graph
3386        // for "output" images/buffers
3387        //
3388        let builtin_final_node =
3389            graph.add_callback_node("BuiltinFinalNode", RenderGraphQueue::DefaultGraphics);
3390
3391        //
3392        // Walk backwards through the DAG, starting from the output images, through all the upstream
3393        // dependencies of those images. We are doing a depth first search. Nodes that make no
3394        // direct or indirect contribution to an output image will not be included. As an
3395        // an implementation detail, we try to put renderpass merge candidates adjacent to each
3396        // other in this list
3397        //
3398
3399        //TODO: Support to force a node to be executed/unculled
3400        let mut node_execution_order = determine_node_order(&graph);
3401        node_execution_order.push(builtin_final_node);
3402
3403        // Print out the execution order
3404        log::trace!("Execution order of unculled nodes:");
3405        for node in &node_execution_order {
3406            log::trace!("  Node {:?} {:?}", node, graph.node(*node).name());
3407        }
3408
3409        //
3410        // Traverse the graph to determine specifications for all images that will be used. This
3411        // iterates forwards and backwards through the node graph. This allows us to specify
3412        // attributes about images (like format, sample count) in key areas and infer it elsewhere.
3413        // If there is not enough information to infer then the render graph cannot be used and
3414        // building it will panic.
3415        //
3416        let mut constraint_results =
3417            determine_constraints(&graph, &node_execution_order, swapchain_surface_info);
3418
3419        // Look at all image versions and ensure a constraint exists for usages where the node was
3420        // not culled
3421        //RenderGraphPlan::verify_unculled_image_usages_specifications_exist(&graph, &node_execution_order, &constraint_results);
3422
3423        // Print out the constraints assigned to images
3424        //print_image_constraints(&graph, &mut constraint_results);
3425
3426        //
3427        // Add resolves to the graph - this will occur when a renderpass outputs a multisample image
3428        // to a renderpass that is expecting a non-multisampled image.
3429        //
3430        insert_resolves(&mut graph, &node_execution_order, &mut constraint_results);
3431
3432        // Print the cases where we can't reuse images
3433        //print_image_compatibility(&graph, &constraint_results);
3434
3435        //
3436        // Assign logical images to physical images. This should give us a minimal number of images
3437        // if we are not reusing or aliasing. (We reuse when we assign physical indexes)
3438        //
3439        let assign_virtual_images_result =
3440            assign_virtual_resources(&graph, &node_execution_order, &mut constraint_results);
3441
3442        //
3443        // Combine nodes into passes where possible
3444        //
3445        let mut passes = build_physical_passes(
3446            &graph,
3447            &node_execution_order,
3448            &constraint_results,
3449            &assign_virtual_images_result,
3450        );
3451
3452        //
3453        // Find virtual images with matching specification and non-overlapping lifetimes. Assign
3454        // the same physical index to them so that we reuse a single allocation
3455        //
3456        let assign_physical_resources_result = assign_physical_resources(
3457            &graph,
3458            &constraint_results,
3459            &assign_virtual_images_result,
3460            &mut passes,
3461        );
3462
3463        //
3464        // Determine read/write barriers for each node based on the data the produce/consume
3465        //
3466        let node_barriers = build_node_barriers(
3467            &graph,
3468            &node_execution_order,
3469            &constraint_results,
3470            &assign_physical_resources_result, /*, &determine_image_layouts_result*/
3471            builtin_final_node,
3472        );
3473
3474        print_node_barriers(&node_barriers);
3475
3476        //TODO: Figure out in/out layouts for passes? Maybe insert some other fixes? Drop transient
3477        // images?
3478
3479        //
3480        // Combine the node barriers to produce the dependencies for subpasses and determine/handle
3481        // image layout transitions
3482        //
3483        build_pass_barriers(
3484            &graph,
3485            &node_execution_order,
3486            &constraint_results,
3487            &assign_physical_resources_result,
3488            &node_barriers,
3489            &mut passes,
3490        );
3491
3492        //TODO: Cull images that only exist within the lifetime of a single pass? (just passed among
3493        // subpasses)
3494
3495        //TODO: Allocation of images
3496        // alias_images(
3497        //     &graph,
3498        //     &node_execution_order,
3499        //     &constraint_results,
3500        //     &assign_physical_resources_result,
3501        //     &node_barriers,
3502        //     &passes,
3503        // );
3504
3505        //
3506        // Produce the final output data. This mainly includes a descriptor object that can be
3507        // passed into the resource system to create the renderpass but also includes other metadata
3508        // required to push them through the command queue
3509        //
3510        let output_passes = create_output_passes(&graph, passes);
3511
3512        //
3513        // Separate the output images from the intermediate images (the rendergraph will be
3514        // responsible for allocating the intermediate images)
3515        //
3516        let mut external_images: FnvHashMap<PhysicalImageViewId, RenderGraphPlanExternalImage> =
3517            Default::default();
3518        let mut external_image_physical_ids = FnvHashSet::default();
3519        for external_image in &graph.external_images {
3520            let input_physical_view_id = external_image
3521                .input_usage
3522                .map(|usage| assign_physical_resources_result.image_usage_to_image_view[&usage]);
3523
3524            let output_physical_view_id = external_image
3525                .output_usage
3526                .map(|usage| assign_physical_resources_result.image_usage_to_image_view[&usage]);
3527
3528            if let Some(physical_view_id) = input_physical_view_id.or(output_physical_view_id) {
3529                // Verify that one of them was None, or that they were the same
3530                assert!(
3531                    input_physical_view_id.is_none()
3532                        || output_physical_view_id.is_none()
3533                        || input_physical_view_id == output_physical_view_id
3534                );
3535
3536                external_images.insert(
3537                    physical_view_id,
3538                    RenderGraphPlanExternalImage {
3539                        id: external_image.external_image_id,
3540                        resource: external_image.image_resource.clone(),
3541                    },
3542                );
3543
3544                external_image_physical_ids.insert(
3545                    assign_physical_resources_result.image_views[physical_view_id.0].physical_image,
3546                );
3547            }
3548        }
3549
3550        let mut external_buffers: FnvHashMap<PhysicalBufferId, RenderGraphPlanExternalBuffer> =
3551            Default::default();
3552        //let mut external_buffer_physical_ids = FnvHashSet::default();
3553        for external_buffer in &graph.external_buffers {
3554            let input_physical_id = external_buffer
3555                .input_usage
3556                .map(|usage| assign_physical_resources_result.buffer_usage_to_physical[&usage]);
3557
3558            let output_physical_id = external_buffer
3559                .output_usage
3560                .map(|usage| assign_physical_resources_result.buffer_usage_to_physical[&usage]);
3561
3562            if let Some(physical_id) = input_physical_id.or(output_physical_id) {
3563                // Verify that one of them was None, or that they were the same
3564                assert!(
3565                    input_physical_id.is_none()
3566                        || output_physical_id.is_none()
3567                        || input_physical_id == output_physical_id
3568                );
3569
3570                external_buffers.insert(
3571                    physical_id,
3572                    RenderGraphPlanExternalBuffer {
3573                        id: external_buffer.external_buffer_id,
3574                        resource: external_buffer.buffer_resource.clone(),
3575                    },
3576                );
3577            }
3578        }
3579
3580        let mut intermediate_images: FnvHashMap<PhysicalImageId, RenderGraphImageSpecification> =
3581            Default::default();
3582        for (index, specification) in assign_physical_resources_result
3583            .image_specifications
3584            .iter()
3585            .enumerate()
3586        {
3587            let physical_image = PhysicalImageId(index);
3588
3589            if external_image_physical_ids.contains(&physical_image) {
3590                continue;
3591            }
3592
3593            intermediate_images.insert(physical_image, specification.clone());
3594        }
3595
3596        let mut intermediate_buffers: FnvHashMap<PhysicalBufferId, RenderGraphBufferSpecification> =
3597            Default::default();
3598        for (index, specification) in assign_physical_resources_result
3599            .buffer_specifications
3600            .iter()
3601            .enumerate()
3602        {
3603            let physical_buffer = PhysicalBufferId(index);
3604
3605            if external_buffers.contains_key(&physical_buffer) {
3606                continue;
3607            }
3608
3609            intermediate_buffers.insert(physical_buffer, specification.clone());
3610        }
3611
3612        print_final_images(
3613            &assign_physical_resources_result,
3614            &external_images,
3615            &intermediate_images,
3616        );
3617        print_final_buffers(&external_buffers, &intermediate_buffers);
3618
3619        print_final_resource_usages(
3620            &graph,
3621            &assign_physical_resources_result,
3622            &constraint_results,
3623            &output_passes,
3624        );
3625
3626        //
3627        // Create a lookup from node_id to pass. Nodes are culled and renderpasses may include
3628        // subpasses from multiple nodes.
3629        //
3630        let mut node_to_pass_index = FnvHashMap::default();
3631        for (pass_index, pass) in output_passes.iter().enumerate() {
3632            node_to_pass_index.insert(pass.node(), pass_index);
3633        }
3634
3635        RenderGraphPlan {
3636            passes: output_passes,
3637            external_images,
3638            external_buffers,
3639            intermediate_images,
3640            intermediate_buffers,
3641            image_views: assign_physical_resources_result.image_views,
3642            node_to_pass_index,
3643            _image_usage_to_physical: assign_physical_resources_result.image_usage_to_physical,
3644            image_usage_to_view: assign_physical_resources_result.image_usage_to_image_view,
3645            buffer_usage_to_physical: assign_physical_resources_result.buffer_usage_to_physical,
3646
3647            visit_node_callbacks: graph.visit_node_callbacks,
3648            _render_phase_dependencies: graph.render_phase_dependencies,
3649        }
3650    }
3651}