Skip to content

Commit

Permalink
Graph Visualization: Added hierarchy based on frames, faster function
Browse files Browse the repository at this point in the history
to get the 'pbtxt' and 'dot' formats, and implicit edges are added
between commands and subcommands.

Hierarchy based on frames: Using a depth first search starting in every
VK_QUEUE_PRESENT command.

Implicit edges added between commands and subcommands to correctly
defined the frames in the depth first search.
  • Loading branch information
elviscapiaq committed Jan 11, 2019
1 parent 101b8b3 commit 6e34820
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 44 deletions.
133 changes: 96 additions & 37 deletions gapis/resolve/dependencygraph2/graph_visualization/graph_structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@
package graph_visualization

import (
"bytes"
"fmt"
"sort"
"strconv"
)

const (
NO_VISITED = 0
VISITED_AND_USED = -1
FRAME = "FRAME"
UNUSED = "UNUSED"
)

type node struct {
Expand All @@ -33,6 +35,8 @@ type node struct {
label string
name string
attributes string
isEndOfFrame bool
subCommandNodes []*node
}

type edge struct {
Expand Down Expand Up @@ -89,6 +93,10 @@ func getNewNode(id int, label string) *node {
return newNode
}

func (currentNode *node) addSubCommandNode(subCommandNode *node) {
currentNode.subCommandNodes = append(currentNode.subCommandNodes, subCommandNode)
}

func (g *graph) addEdge(newEdge *edge) {
source, sink := newEdge.source, newEdge.sink
if _, ok := source.outNeighbourIdToEdgeId[sink.id]; ok {
Expand Down Expand Up @@ -257,60 +265,111 @@ func (g *graph) makeStronglyConnectedComponentsByCommandTypeId() {
}
}

func (g *graph) getEdgesInDotFormat() string {
output := ""
for _, currentEdge := range g.edgeIdToEdge {
lines := strconv.Itoa(currentEdge.source.id) + " -> " + strconv.Itoa(currentEdge.sink.id) + ";\n"
output += lines
func (g *graph) dfs(currentNode *node, visited []bool, visitedNodes *[]*node) {
*visitedNodes = append(*visitedNodes, currentNode)
visited[currentNode.id] = true
neighbours := g.getSortedNeighbours(currentNode.outNeighbourIdToEdgeId)
for _, neighbour := range neighbours {
if !visited[neighbour.id] {
g.dfs(neighbour, visited, visitedNodes)
}
}

for _, subCommandNode := range currentNode.subCommandNodes {
if !visited[subCommandNode.id] {
g.dfs(subCommandNode, visited, visitedNodes)
}
}
return output
}

func (g *graph) getNodesInDotFormat() string {
output := ""
for _, currentNode := range g.nodeIdToNode {
lines := strconv.Itoa(currentNode.id) + "[label=" + currentNode.label + "]" + ";\n"
output += lines
func (g *graph) joinNodesByFrame() {
visited := make([]bool, g.maxNodeId+1)
frameNumber := 1
nodes := g.getSortedNodes()
for _, currentNode := range nodes {
if !visited[currentNode.id] && currentNode.isEndOfFrame {
visitedNodes := []*node{}
g.dfs(currentNode, visited, &visitedNodes)
for _, visitedNode := range visitedNodes {
visitedNode.label = fmt.Sprintf("%s%d/%s", FRAME, frameNumber, visitedNode.label)
}
frameNumber++
}
}
return output
}

func (g *graph) getGraphInDotFormat() string {
output := "digraph g {\n"
output += g.getNodesInDotFormat()
output += g.getEdgesInDotFormat()
output += "}\n"
return output
func (g *graph) joinNodesWithZeroDegree() {
for _, currentNode := range g.nodeIdToNode {
if (len(currentNode.inNeighbourIdToEdgeId) + len(currentNode.outNeighbourIdToEdgeId)) == 0 {
currentNode.label = UNUSED + "/" + currentNode.label
}
}
}

func (g *graph) getGraphInPbtxtFormat() string {
func (g *graph) getSortedNodes() []*node {
nodes := []*node{}
for _, currentNode := range g.nodeIdToNode {
nodes = append(nodes, currentNode)
}
sort.Sort(nodeSorter(nodes))
return nodes
}

output := ""
for _, currentNode := range nodes {
lines := "node {\n"
lines += "name: \"" + currentNode.label + "\"\n"
lines += "op: \"" + currentNode.label + "\"\n"
func (g *graph) getSortedNeighbours(neighbourIdToEdgeId map[int]int) []*node {
neighbours := []*node{}
for neighbourId := range neighbourIdToEdgeId {
neighbours = append(neighbours, g.nodeIdToNode[neighbourId])
}
sort.Sort(nodeSorter(neighbours))
return neighbours
}

neighbours := []*node{}
for neighbourId := range currentNode.inNeighbourIdToEdgeId {
neighbours = append(neighbours, g.nodeIdToNode[neighbourId])
func (g *graph) getEdgesInDotFormat() []byte {
nodes := g.getSortedNodes()
var output bytes.Buffer
for _, currentNode := range nodes {
inNeighbours := g.getSortedNeighbours(currentNode.inNeighbourIdToEdgeId)
for _, neighbour := range inNeighbours {
output.WriteString(fmt.Sprintf("%d -> %d;\n", neighbour.id, currentNode.id))
}
sort.Sort(nodeSorter(neighbours))
}
return output.Bytes()
}

func (g *graph) getNodesInDotFormat() []byte {
nodes := g.getSortedNodes()
var output bytes.Buffer
for _, currentNode := range nodes {
output.WriteString(fmt.Sprintf("%d[label=%s];\n", currentNode.id, currentNode.label))
}
return output.Bytes()
}

func (g *graph) getGraphInDotFormat() []byte {
var output bytes.Buffer
output.WriteString("digraph g {\n")
output.Write(g.getNodesInDotFormat())
output.Write(g.getEdgesInDotFormat())
output.WriteString("}\n")
return output.Bytes()
}

func (g *graph) getGraphInPbtxtFormat() []byte {
nodes := g.getSortedNodes()
var output bytes.Buffer
for _, currentNode := range nodes {
output.WriteString("node {\n")
output.WriteString("name: \"" + currentNode.label + "\"\n")
output.WriteString("op: \"" + currentNode.label + "\"\n")

neighbours := g.getSortedNeighbours(currentNode.inNeighbourIdToEdgeId)
for _, neighbour := range neighbours {
lines += "input: \"" + neighbour.label + "\"\n"
output.WriteString("input: \"" + neighbour.label + "\"\n")
}
lines += "attr {\n"
lines += "key: " + "\"" + currentNode.attributes + "\"\n"
lines += "}\n"

lines += "}\n"
output += lines
output.WriteString("attr {\n")
output.WriteString("key: " + "\"" + currentNode.attributes + "\"\n")
output.WriteString("}\n")
output.WriteString("}\n")
}
return output
return output.Bytes()
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ const (
EMPTY = "Empty"
)

func createGraphFromDependencyGraph(dependencyGraph dependencygraph2.DependencyGraph) (*graph, error) {
func createGraphFromDependencyGraph(ctx context.Context, dependencyGraph dependencygraph2.DependencyGraph) (*graph, error) {

currentGraph := createGraph(0)
idToBuilder := map[api.ID]api.GraphVisualizationBuilder{}
commandNameAndNodeIdToNodeId := map[string]int{}

err := dependencyGraph.ForeachNode(
func(nodeId dependencygraph2.NodeID, dependencyNode dependencygraph2.Node) error {
Expand All @@ -49,16 +50,23 @@ func createGraphFromDependencyGraph(dependencyGraph dependencygraph2.DependencyG
idToBuilder[command.API().ID()] = graphVisualizationAPI.GetGraphVisualizationBuilder()
}
builder := idToBuilder[command.API().ID()]
commandNameAndNodeId := fmt.Sprintf("%s_%d", command.CmdName(), cmdNodeId)
isSubCommand, parentNodeId := false, 0

label, name := "", ""
if len(cmdNode.Index) == 1 {
label = builder.GetCommandLabel(command, cmdNodeId)
name = command.CmdName()
commandNameAndNodeIdToNodeId[commandNameAndNodeId] = int(nodeId)
} else if len(cmdNode.Index) > 1 {
commandName := fmt.Sprintf("%s_%d", command.CmdName(), cmdNodeId)
dependencyNodeAccesses := dependencyGraph.GetNodeAccesses(nodeId)
subCommandName := EMPTY
if len(dependencyNodeAccesses.InitCmdNodes) > 0 {
if id, ok := commandNameAndNodeIdToNodeId[commandNameAndNodeId]; ok {
isSubCommand = true
parentNodeId = id
}

subCmdDependencyNode := dependencyGraph.GetNode(dependencyNodeAccesses.InitCmdNodes[0])
if subCmdNode, ok := subCmdDependencyNode.(dependencygraph2.CmdNode); ok {
if len(subCmdNode.Index) == 0 {
Expand All @@ -69,7 +77,7 @@ func createGraphFromDependencyGraph(dependencyGraph dependencygraph2.DependencyG
subCommandName = subCmd.CmdName()
}
}
label = builder.GetSubCommandLabel(cmdNode.Index, commandName, subCommandName)
label = builder.GetSubCommandLabel(cmdNode.Index, commandNameAndNodeId, subCommandName)
name = subCommandName
}

Expand All @@ -81,6 +89,11 @@ func createGraphFromDependencyGraph(dependencyGraph dependencygraph2.DependencyG
newNode := getNewNode(int(nodeId), label)
newNode.name = name
newNode.attributes = attributes
newNode.isEndOfFrame = command.CmdFlags(ctx, api.CmdID(cmdNodeId), &api.GlobalState{}).IsEndOfFrame()
if isSubCommand {
parentNode := currentGraph.nodeIdToNode[parentNodeId]
parentNode.addSubCommandNode(newNode)
}
currentGraph.addNode(newNode)
}
}
Expand Down Expand Up @@ -110,14 +123,15 @@ func GetGraphVisualizationFromCapture(ctx context.Context, p *path.Capture, form
return []byte{}, err
}

currentGraph, err := createGraphFromDependencyGraph(dependencyGraph)
currentGraph.removeNodesWithZeroDegree()
output := ""
currentGraph, err := createGraphFromDependencyGraph(ctx, dependencyGraph)
currentGraph.joinNodesByFrame()
currentGraph.joinNodesWithZeroDegree()
output := []byte{}
if format == service.GraphFormat_PBTXT {
output = currentGraph.getGraphInPbtxtFormat()
} else if format == service.GraphFormat_DOT {
output = currentGraph.getGraphInDotFormat()
}

return []byte(output), err
return output, err
}

0 comments on commit 6e34820

Please sign in to comment.