fn visit_node(
    graph: &RenderGraphBuilder,
    node_id: RenderGraphNodeId,
    visited: &mut Vec<bool>,
    visiting: &mut Vec<bool>,
    visiting_stack: &mut Vec<RenderGraphNodeId>,
    ordered_list: &mut Vec<RenderGraphNodeId>,
) {
    // This node is already visited and inserted into ordered_list
    if visited[node_id.0] {
        return;
    }

    // This node is already being visited higher up in the stack. This indicates a cycle in the
    // graph
    if visiting[node_id.0] {
        log::trace!("Found cycle in graph");
        log::trace!("{:?}", graph.node(node_id));
        for v in visiting_stack.iter().rev() {
            log::trace!("{:?}", graph.node(*v));
        }
        panic!("Graph has a cycle");
    }

    // When we enter the node, mark the node as being in-progress of being visited to help
    // detect cycles in the graph
    visiting[node_id.0] = true;
    visiting_stack.push(node_id);

    //
    // Visit children
    //
    //log::trace!("  Begin visit {:?}", node_id);
    let node = graph.node(node_id);

    // The order of visiting nodes here matters. If we consider merging subpasses, trying to
    // visit a node we want to merge with last will show that there are no indirect dependencies
    // between the merge candidate and the node we are visiting now. However, if there are
    // indirect dependencies, visiting a different node might mark the merge candidate as
    // already visited. So while we could try to visit the merge candidate last, it won't
    // guarantee that the merge candidate will actually be inserted later in the ordered_list
    // than any other dependency
    //
    // Also if we are trying to merge with a dependency (which will execute before us), then any
    // other requirements we have will need to be fulfilled before that node starts
    //
    // Other priorities might be to front-load anything compute-light/GPU-heavy. We can do this
    // by some sort of flagging/priority system to influence this logic. We could also expose
    // a way for end-users to add arbitrary dependencies for the sole purpose of influencing the
    // ordering here.
    //
    // As a first pass implementation, just ensure any merge dependencies are visited last so
    // that we will be more likely to be able to merge passes

    //
    // Delay visiting these nodes so that we get the best chance possible of mergeable passes
    // being adjacent to each other in the orderered list
    //
    let mut merge_candidates = FnvHashSet::default();
    if let Some(depth_attachment) = &node.depth_attachment {
        if let Some(read_image) = depth_attachment.read_image {
            let upstream_node = graph.image_version_info(read_image).creator_node;

            // This might be too expensive to check
            if can_passes_merge(graph, upstream_node, node.id()) {
                merge_candidates.insert(upstream_node);
            }
        }
    }

    for color_attachment in &node.color_attachments {
        // If this is an attachment we are reading, then the node that created it is a merge candidate
        if let Some(read_image) = color_attachment.as_ref().and_then(|x| x.read_image) {
            let upstream_node = graph.image_version_info(read_image).creator_node;

            // This might be too expensive to check
            if can_passes_merge(graph, upstream_node, node.id()) {
                merge_candidates.insert(upstream_node);
            }
        }
    }

    //
    // Visit all the nodes we aren't delaying
    //
    for read in &node.image_reads {
        let upstream_node = graph.image_version_info(read.image).creator_node;
        if !merge_candidates.contains(&upstream_node) {
            visit_node(
                graph,
                upstream_node,
                visited,
                visiting,
                visiting_stack,
                ordered_list,
            );
        }
    }

    for modify in &node.image_modifies {
        let upstream_node = graph.image_version_info(modify.input).creator_node;
        if !merge_candidates.contains(&upstream_node) {
            visit_node(
                graph,
                upstream_node,
                visited,
                visiting,
                visiting_stack,
                ordered_list,
            );
        }
    }

    for sampled_image in &node.sampled_images {
        let upstream_node = graph.image_version_info(*sampled_image).creator_node;
        if !merge_candidates.contains(&upstream_node) {
            visit_node(
                graph,
                upstream_node,
                visited,
                visiting,
                visiting_stack,
                ordered_list,
            );
        }
    }

    for read in &node.buffer_reads {
        let upstream_node = graph.buffer_version_info(read.buffer).creator_node;
        if !merge_candidates.contains(&upstream_node) {
            visit_node(
                graph,
                upstream_node,
                visited,
                visiting,
                visiting_stack,
                ordered_list,
            );
        }
    }

    for modify in &node.buffer_modifies {
        let upstream_node = graph.buffer_version_info(modify.input).creator_node;
        if !merge_candidates.contains(&upstream_node) {
            visit_node(
                graph,
                upstream_node,
                visited,
                visiting,
                visiting_stack,
                ordered_list,
            );
        }
    }

    //
    // Now visit the nodes we delayed visiting
    //
    for merge_candidate in merge_candidates {
        visit_node(
            graph,
            merge_candidate,
            visited,
            visiting,
            visiting_stack,
            ordered_list,
        );
    }

    // All our pre-requisites were visited, so it's now safe to push this node onto the
    // orderered list
    ordered_list.push(node_id);
    visited[node_id.0] = true;

    // We are no longer visiting this node
    //log::trace!("  End visit {:?}", node_id);
    visiting_stack.pop();
    visiting[node_id.0] = false;
}
// Recursively called to topologically sort the nodes to determine execution order. See
// determine_node_order which kicks this off.
// https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search
Recursively called to topologically sort the nodes to determine execution order. See
determine_node_order which kicks this off.
Recursively called to topologically sort the nodes to determine execution order . See determine_node_order which kicks this off .
fn visit_node(
    graph: &RenderGraphBuilder,
    node_id: RenderGraphNodeId,
    visited: &mut Vec<bool>,
    visiting: &mut Vec<bool>,
    visiting_stack: &mut Vec<RenderGraphNodeId>,
    ordered_list: &mut Vec<RenderGraphNodeId>,
) {
    // This node is already visited and inserted into ordered_list
    if visited[node_id.0] {
        return;
    }
    // This node is already being visited higher up in the stack. This indicates a cycle in the
    // graph
    if visiting[node_id.0] {
        log::trace!("Found cycle in graph");
        log::trace!("{:?}", graph.node(node_id));
        for v in visiting_stack.iter().rev() {
            log::trace!("{:?}", graph.node(*v));
        }
        panic!("Graph has a cycle");
    }
    // When we enter the node, mark the node as being in-progress of being visited to help
    // detect cycles in the graph
    visiting[node_id.0] = true;
    visiting_stack.push(node_id);
    //
    // Visit children
    //
    //log::trace!("  Begin visit {:?}", node_id);
    let node = graph.node(node_id);
    // The order of visiting nodes here matters. If we consider merging subpasses, trying to
    // visit a node we want to merge with last will show that there are no indirect dependencies
    // between the merge candidate and the node we are visiting now. However, if there are
    // indirect dependencies, visiting a different node might mark the merge candidate as
    // already visited. So while we could try to visit the merge candidate last, it won't
    // guarantee that the merge candidate will actually be inserted later in the ordered_list
    // than any other dependency
    //
    // Also if we are trying to merge with a dependency (which will execute before us), then any
    // other requirements we have will need to be fulfilled before that node starts
    //
    // Other priorities might be to front-load anything compute-light/GPU-heavy. We can do this
    // by some sort of flagging/priority system to influence this logic. We could also expose
    // a way for end-users to add arbitrary dependencies for the sole purpose of influencing the
    // ordering here.
    //
    // As a first pass implementation, just ensure any merge dependencies are visited last so
    // that we will be more likely to be able to merge passes
    //
    // Delay visiting these nodes so that we get the best chance possible of mergeable passes
    // being adjacent to each other in the orderered list
    //
    let mut merge_candidates = FnvHashSet::default();
    if let Some(depth_attachment) = &node.depth_attachment {
        if let Some(read_image) = depth_attachment.read_image {
            let upstream_node = graph.image_version_info(read_image).creator_node;
            // This might be too expensive to check
            if can_passes_merge(graph, upstream_node, node.id()) {
                merge_candidates.insert(upstream_node);
            }
        }
    }
    for color_attachment in &node.color_attachments {
        // If this is an attachment we are reading, then the node that created it is a merge candidate
        if let Some(read_image) = color_attachment.as_ref().and_then(|x| x.read_image) {
            let upstream_node = graph.image_version_info(read_image).creator_node;
            // This might be too expensive to check
            if can_passes_merge(graph, upstream_node, node.id()) {
                merge_candidates.insert(upstream_node);
            }
        }
    }
    //
    // Visit all the nodes we aren't delaying
    //
    for read in &node.image_reads {
        let upstream_node = graph.image_version_info(read.image).creator_node;
        if !merge_candidates.contains(&upstream_node) {
            visit_node(
                graph,
                upstream_node,
                visited,
                visiting,
                visiting_stack,
                ordered_list,
            );
        }
    }
    for modify in &node.image_modifies {
        let upstream_node = graph.image_version_info(modify.input).creator_node;
        if !merge_candidates.contains(&upstream_node) {
            visit_node(
                graph,
                upstream_node,
                visited,
                visiting,
                visiting_stack,
                ordered_list,
            );
        }
    }
    for sampled_image in &node.sampled_images {
        let upstream_node = graph.image_version_info(*sampled_image).creator_node;
        if !merge_candidates.contains(&upstream_node) {
            visit_node(
                graph,
                upstream_node,
                visited,
                visiting,
                visiting_stack,
                ordered_list,
            );
        }
    }
    for read in &node.buffer_reads {
        let upstream_node = graph.buffer_version_info(read.buffer).creator_node;
        if !merge_candidates.contains(&upstream_node) {
            visit_node(
                graph,
                upstream_node,
                visited,
                visiting,
                visiting_stack,
                ordered_list,
            );
        }
    }
    for modify in &node.buffer_modifies {
        let upstream_node = graph.buffer_version_info(modify.input).creator_node;
        if !merge_candidates.contains(&upstream_node) {
            visit_node(
                graph,
                upstream_node,
                visited,
                visiting,
                visiting_stack,
                ordered_list,
            );
        }
    }
    //
    // Now visit the nodes we delayed visiting
    //
    for merge_candidate in merge_candidates {
        visit_node(
            graph,
            merge_candidate,
            visited,
            visiting,
            visiting_stack,
            ordered_list,
        );
    }
    // All our pre-requisites were visited, so it's now safe to push this node onto the
    // orderered list
    ordered_list.push(node_id);
    visited[node_id.0] = true;
    // We are no longer visiting this node
    //log::trace!("  End visit {:?}", node_id);
    visiting_stack.pop();
    visiting[node_id.0] = false;
}
fn visit_node ( graph : & RenderGraphBuilder , node_id : RenderGraphNodeId , visited : & mut Vec < bool > , visiting : & mut Vec < bool > , visiting_stack : & mut Vec < RenderGraphNodeId > , ordered_list : & mut Vec < RenderGraphNodeId > , ) { // This node is already visited and inserted into ordered_list if visited [ node_id . 0 ] { return ; } // This node is already being visited higher up in the stack. This indicates a cycle in the // graph if visiting [ node_id . 0 ] { log :: trace ! ( " " ) ; log :: trace ! ( " " graph node ( node_id ) ) ; for v in visiting_stack . iter ( ) . rev ( ) { log :: trace ! ( " " graph node ( v ) ) ; } panic ! ( " " ) ; } // When we enter the node, mark the node as being in-progress of being visited to help // detect cycles in the graph visiting [ node_id . 0 ] = true ; visiting_stack . push ( node_id ) ; // // Visit children // //log::trace!("  Begin visit {:?}", node_id); let node = graph . node ( node_id ) ; // The order of visiting nodes here matters. If we consider merging subpasses, trying to // visit a node we want to merge with last will show that there are no indirect dependencies // between the merge candidate and the node we are visiting now. However, if there are // indirect dependencies, visiting a different node might mark the merge candidate as // already visited. So while we could try to visit the merge candidate last, it won't // guarantee that the merge candidate will actually be inserted later in the ordered_list // than any other dependency // // Also if we are trying to merge with a dependency (which will execute before us), then any // other requirements we have will need to be fulfilled before that node starts // // Other priorities might be to front-load anything compute-light/GPU-heavy. We can do this // by some sort of flagging/priority system to influence this logic. We could also expose // a way for end-users to add arbitrary dependencies for the sole purpose of influencing the // ordering here. // // As a first pass implementation, just ensure any merge dependencies are visited last so // that we will be more likely to be able to merge passes // // Delay visiting these nodes so that we get the best chance possible of mergeable passes // being adjacent to each other in the orderered list // let mut merge_candidates = FnvHashSet :: default ( ) ; if let Some ( depth_attachment ) = & node . depth_attachment { if let Some ( read_image ) = depth_attachment . read_image { let upstream_node = graph . image_version_info ( read_image ) . creator_node ; // This might be too expensive to check if can_passes_merge ( graph , upstream_node , node . id ( ) ) { merge_candidates . insert ( upstream_node ) ; } } } for color_attachment in & node . color_attachments { // If this is an attachment we are reading, then the node that created it is a merge candidate if let Some ( read_image ) = color_attachment . as_ref ( ) . and_then ( | x | x . read_image ) { let upstream_node = graph . image_version_info ( read_image ) . creator_node ; // This might be too expensive to check if can_passes_merge ( graph , upstream_node , node . id ( ) ) { merge_candidates . insert ( upstream_node ) ; } } } // // Visit all the nodes we aren't delaying // for read in & node . image_reads { let upstream_node = graph . image_version_info ( read . image ) . creator_node ; if ! merge_candidates . contains ( & upstream_node ) { visit_node ( graph , upstream_node , visited , visiting , visiting_stack , ordered_list , ) ; } } for modify in & node . image_modifies { let upstream_node = graph . image_version_info ( modify . input ) . creator_node ; if ! merge_candidates . contains ( & upstream_node ) { visit_node ( graph , upstream_node , visited , visiting , visiting_stack , ordered_list , ) ; } } for sampled_image in & node . sampled_images { let upstream_node = graph . image_version_info ( * sampled_image ) . creator_node ; if ! merge_candidates . contains ( & upstream_node ) { visit_node ( graph , upstream_node , visited , visiting , visiting_stack , ordered_list , ) ; } } for read in & node . buffer_reads { let upstream_node = graph . buffer_version_info ( read . buffer ) . creator_node ; if ! merge_candidates . contains ( & upstream_node ) { visit_node ( graph , upstream_node , visited , visiting , visiting_stack , ordered_list , ) ; } } for modify in & node . buffer_modifies { let upstream_node = graph . buffer_version_info ( modify . input ) . creator_node ; if ! merge_candidates . contains ( & upstream_node ) { visit_node ( graph , upstream_node , visited , visiting , visiting_stack , ordered_list , ) ; } } // // Now visit the nodes we delayed visiting // for merge_candidate in merge_candidates { visit_node ( graph , merge_candidate , visited , visiting , visiting_stack , ordered_list , ) ; } // All our pre-requisites were visited, so it's now safe to push this node onto the // orderered list ordered_list . push ( node_id ) ; visited [ node_id . 0 ] = true ; // We are no longer visiting this node //log::trace!("  End visit {:?}", node_id); visiting_stack . pop ( ) ; visiting [ node_id . 0 ] = false ; }
Recursively called to topologically sort the nodes to determine execution order.
Recursively called to topologically sort the nodes to determine execution order .
graph
&RenderGraphBuilder
node_id
RenderGraphNodeId
visited
&mut Vec<bool>
visiting
&mut Vec<bool>
visiting_stack
&mut Vec<RenderGraphNodeId>
ordered_list
&mut Vec<RenderGraphNodeId>
graph
&RenderGraphBuilder
node_id
RenderGraphNodeId
visited
&mut Vec<bool>
visiting
&mut Vec<bool>
visiting_stack
&mut Vec<RenderGraphNodeId>
ordered_list
&mut Vec<RenderGraphNodeId>