diff --git a/gapis/api/graph_visualization.go b/gapis/api/graph_visualization.go index 462fc840ed..ec1c466c32 100644 --- a/gapis/api/graph_visualization.go +++ b/gapis/api/graph_visualization.go @@ -14,50 +14,156 @@ package api +import ( + "bytes" + "fmt" +) + +// Label describes the levels of hierarchy for nodes in the graph +// visualization using TensorBoard which reads pbtxt format. +type Label struct { + // LevelsName is the name for each level that node belongs + // from top level to down level. + LevelsName []string + + // LevelsID is the ID for each level that node belongs + // from top level to down level. + LevelsID []int +} + +// GetSize returns the number of levels. +func (label *Label) GetSize() int { + return len(label.LevelsName) +} + +// PushBack adds a level in the back of the current Label. +func (label *Label) PushBack(name string, id int) { + label.LevelsName = append(label.LevelsName, name) + label.LevelsID = append(label.LevelsID, id) +} + +// PushFront adds a level in the front of the current Label.. +func (label *Label) PushFront(name string, id int) { + newLabel := &Label{LevelsName: []string{name}, LevelsID: []int{id}} + newLabel.PushBackLabel(label) + label.LevelsName = newLabel.LevelsName + label.LevelsID = newLabel.LevelsID +} + +// PushBackLabel adds a Label in the back of the current Label. +func (label *Label) PushBackLabel(labelToPush *Label) { + label.LevelsName = append(label.LevelsName, labelToPush.LevelsName...) + label.LevelsID = append(label.LevelsID, labelToPush.LevelsID...) +} + +// Insert a new level in the current Label. +func (label *Label) Insert(level int, name string, id int) { + if level < len(label.LevelsName) { + label.LevelsName = append(label.LevelsName, "") + label.LevelsID = append(label.LevelsID, 0) + copy(label.LevelsName[level+1:], label.LevelsName[level:]) + copy(label.LevelsID[level+1:], label.LevelsID[level:]) + label.LevelsName[level] = name + label.LevelsID[level] = id + } +} + +// GetCommandName returns the name of the last level +// corresponding to the node name. +func (label *Label) GetCommandName() string { + if len(label.LevelsName) > 0 { + return label.LevelsName[len(label.LevelsName)-1] + } + return "" +} + +// GetCommandId returns the ID of the last level +// corresponding to the node ID. +func (label *Label) GetCommandId() int { + if len(label.LevelsID) > 0 { + return label.LevelsID[len(label.LevelsID)-1] + } + return 0 +} + +// GetLabelAsAString returns the Label as a string concatenating +// names and ID for each level delimited by '/'. +func (label *Label) GetLabelAsAString() string { + var output bytes.Buffer + for i := range label.LevelsID { + output.WriteString(label.LevelsName[i]) + fmt.Fprintf(&output, "%d", label.LevelsID[i]) + if i+1 < len(label.LevelsID) { + output.WriteString("/") + } + } + return output.String() +} + +// Hierarchy describes the levels ID of hierarchy for vulkan +// commands and vulkan subcommands. type Hierarchy struct { LevelsID []int } +// GetSize returns the number of levels in Hierarchy. func (h *Hierarchy) GetSize() int { return len(h.LevelsID) } +// GetID returns ID for a specific level, indexed from 1. func (h *Hierarchy) GetID(level int) int { return h.LevelsID[level-1] } +// PopBack removes the last level in Hierarchy. func (h *Hierarchy) PopBack() { if len(h.LevelsID) > 0 { h.LevelsID = h.LevelsID[:len(h.LevelsID)-1] } } +// PushBackToResize keeps adding a new level in the back +// until to get newSize levels in Hierarchy. func (h *Hierarchy) PushBackToResize(newSize int) { for len(h.LevelsID) < newSize { h.LevelsID = append(h.LevelsID, 0) } } +// PopBackToResize keeps removing the back level until +// to get newSize levels in Hierarchy. func (h *Hierarchy) PopBackToResize(newSize int) { for len(h.LevelsID) > newSize { h.PopBack() } } +// IncreaseIDByOne increases in one a level ID, indexed from 1. func (h *Hierarchy) IncreaseIDByOne(level int) { h.LevelsID[level-1]++ } +// HierarchyNames describes the levels name of Hierarchy for +// vulkan commands and vulkan subcommands. type HierarchyNames struct { + // BeginNameToLevel are the vulkan commands name that begin a new level. BeginNameToLevel map[string]int - EndNameToLevel map[string]int - NameOfLevels []string + + // EndNameToLevel are the vulkan commands name that end a new level. + EndNameToLevel map[string]int + + // NameOfLevels are the names assigned to new levels. + NameOfLevels []string } +// GetName returns name for a specific level, indexed from 1. func (hierarchyNames *HierarchyNames) GetName(level int) string { return hierarchyNames.NameOfLevels[level-1] } +// PushBack adds in the back a new level with beginName, +// endName and the name for this level. func (hierarchyNames *HierarchyNames) PushBack(beginName, endName, name string) { size := len(hierarchyNames.NameOfLevels) + 1 hierarchyNames.BeginNameToLevel[beginName] = size @@ -65,12 +171,18 @@ func (hierarchyNames *HierarchyNames) PushBack(beginName, endName, name string) hierarchyNames.NameOfLevels = append(hierarchyNames.NameOfLevels, name) } +// GraphVisualizationAPI is the common interface for graph visualization. type GraphVisualizationAPI interface { + // GetGraphVisualizationBuilder returns a interface to GraphVisualizationBuilder GetGraphVisualizationBuilder() GraphVisualizationBuilder } +// GraphVisualizationBuilder is the common interface used to process commands from +// graphics API in order to get the Label for nodes in the graph visualization. type GraphVisualizationBuilder interface { - GetCommandLabel(command Cmd, commandNodeId uint64) string + // GetCommandLabel returns the Label for the command + GetCommandLabel(command Cmd, cmdId uint64) *Label - GetSubCommandLabel(index SubCmdIdx, commandName string, subCommandName string) string + // GetSubCommandLabel returns the Label for the subcommand + GetSubCommandLabel(index SubCmdIdx, commandName string, cmdId uint64, subCommandName string) *Label } diff --git a/gapis/api/vulkan/graph_visualization.go b/gapis/api/vulkan/graph_visualization.go index 49c4381945..27bf993b37 100644 --- a/gapis/api/vulkan/graph_visualization.go +++ b/gapis/api/vulkan/graph_visualization.go @@ -15,6 +15,7 @@ package vulkan import ( + "bytes" "fmt" "github.com/google/gapid/gapis/api" ) @@ -26,15 +27,18 @@ var ( ) const ( - VK_BEGIN_COMMAND_BUFFER = "vkBeginCommandBuffer" - VK_CMD_BEGIN_RENDER_PASS = "vkCmdBeginRenderPass" - VK_CMD_NEXT_SUBPASS = "vkCmdNextSubpass" - VK_COMMAND_BUFFER = "vkCommandBuffer" - VK_RENDER_PASS = "vkRenderPass" - VK_SUBPASS = "vkSubpass" - VK_END_COMMAND_BUFFER = "vkEndCommandBuffer" - VK_CMD_END_RENDER_PASS = "vkCmdEndRenderPass" - COMMAND_BUFFER = "commandBuffer" + VK_BEGIN_COMMAND_BUFFER = "vkBeginCommandBuffer" + VK_CMD_BEGIN_RENDER_PASS = "vkCmdBeginRenderPass" + VK_CMD_NEXT_SUBPASS = "vkCmdNextSubpass" + VK_COMMAND_BUFFER = "vkCommandBuffer" + VK_RENDER_PASS = "vkRenderPass" + VK_SUBPASS = "vkSubpass" + VK_END_COMMAND_BUFFER = "vkEndCommandBuffer" + VK_CMD_END_RENDER_PASS = "vkCmdEndRenderPass" + COMMAND_BUFFER = "commandBuffer" + VK_CMD_DEBUG_MARKER_BEGIN = "vkCmdDebugMarkerBeginEXT" + VK_CMD_DEBUG_MARKER_END = "vkCmdDebugMarkerEndEXT" + VK_CMD_DEBUG_MARKER = "vkCmdDebugMarker" ) var ( @@ -43,8 +47,13 @@ var ( ) type labelForVulkanCommands struct { - labelToHierarchy map[string]*api.Hierarchy - subCommandIndexNameToHierarchyLabel map[string]string + labelAsAStringToHierarchy map[string]*api.Hierarchy + subCommandIndexNameToHierarchyLabel map[string]*api.Label + commandBufferIdToHierarchy map[VkCommandBuffer]*api.Hierarchy + commandBufferIdToOrderNumber map[VkCommandBuffer]int + labelsInsideDebugMarkers []*api.Label + positionOfDebugMarkersBegin []int + numberOfDebugMarker int } func getCommandHierarchyNames() *api.HierarchyNames { @@ -66,60 +75,131 @@ func getSubCommandHierarchyNames() *api.HierarchyNames { return subCommandHierarchyNames } -func getCommandBuffer(command api.Cmd) string { +func getMaxCommonPrefix(label1 *api.Label, label2 *api.Label) int { + size := len(label1.LevelsID) + if len(label2.LevelsID) < size { + size = len(label2.LevelsID) + } + for i := 0; i < size; i++ { + if label1.LevelsName[i] != label2.LevelsName[i] || label1.LevelsID[i] != label2.LevelsID[i] { + return i + } + } + return size +} + +func addDebugMarker(builder *labelForVulkanCommands, from, to int) { + level := builder.labelsInsideDebugMarkers[len(builder.labelsInsideDebugMarkers)-1].GetSize() - 1 + builder.numberOfDebugMarker++ + for i := from; i <= to; i++ { + builder.labelsInsideDebugMarkers[i].Insert(level, VK_CMD_DEBUG_MARKER, builder.numberOfDebugMarker) + } +} + +func checkDebugMarkers(builder *labelForVulkanCommands, currentLabel *api.Label) { + commandName := currentLabel.GetCommandName() + positionOfLastDebugMarkerBegin := 0 + labelOfLastDebugMarkerBegin := &api.Label{} + if len(builder.positionOfDebugMarkersBegin) > 0 { + positionOfLastDebugMarkerBegin = builder.positionOfDebugMarkersBegin[len(builder.positionOfDebugMarkersBegin)-1] + labelOfLastDebugMarkerBegin = builder.labelsInsideDebugMarkers[positionOfLastDebugMarkerBegin] + } + + if commandName == VK_CMD_DEBUG_MARKER_BEGIN { + if len(builder.positionOfDebugMarkersBegin) > 0 { + if currentLabel.GetSize() < labelOfLastDebugMarkerBegin.GetSize() { + builder.positionOfDebugMarkersBegin = builder.positionOfDebugMarkersBegin[:len(builder.positionOfDebugMarkersBegin)-1] + } + } + builder.labelsInsideDebugMarkers = append(builder.labelsInsideDebugMarkers, currentLabel) + builder.positionOfDebugMarkersBegin = append(builder.positionOfDebugMarkersBegin, len(builder.labelsInsideDebugMarkers)-1) + + } else if commandName == VK_CMD_DEBUG_MARKER_END { + + if len(builder.positionOfDebugMarkersBegin) > 0 { + if currentLabel.GetSize() != labelOfLastDebugMarkerBegin.GetSize() { + builder.positionOfDebugMarkersBegin = builder.positionOfDebugMarkersBegin[:len(builder.positionOfDebugMarkersBegin)-1] + + } else if getMaxCommonPrefix(labelOfLastDebugMarkerBegin, currentLabel) == currentLabel.GetSize()-1 { + builder.labelsInsideDebugMarkers = append(builder.labelsInsideDebugMarkers, currentLabel) + builder.positionOfDebugMarkersBegin = append(builder.positionOfDebugMarkersBegin, len(builder.labelsInsideDebugMarkers)-1) + addDebugMarker(builder, positionOfLastDebugMarkerBegin, len(builder.labelsInsideDebugMarkers)-1) + builder.positionOfDebugMarkersBegin = builder.positionOfDebugMarkersBegin[:len(builder.positionOfDebugMarkersBegin)-1] + } + } + + } else if len(builder.positionOfDebugMarkersBegin) > 0 { + if currentLabel.GetSize() < labelOfLastDebugMarkerBegin.GetSize() { + builder.positionOfDebugMarkersBegin = builder.positionOfDebugMarkersBegin[:len(builder.positionOfDebugMarkersBegin)-1] + } else { + builder.labelsInsideDebugMarkers = append(builder.labelsInsideDebugMarkers, currentLabel) + } + } +} + +func getCommandBufferId(command api.Cmd) (VkCommandBuffer, bool) { parameters := command.CmdParams() for _, parameter := range parameters { if parameter.Name == COMMAND_BUFFER { - commandBuffer := fmt.Sprintf("%s_%d", parameter.Name, parameter.Get()) - return commandBuffer + return parameter.Get().(VkCommandBuffer), true } } - return "" + return 0, false } -func (builder *labelForVulkanCommands) GetCommandLabel(command api.Cmd, commandNodeId uint64) string { +// GetCommandLabel returns the Label for the Vulkan command. +func (builder *labelForVulkanCommands) GetCommandLabel(command api.Cmd, cmdId uint64) *api.Label { + label := &api.Label{} commandName := command.CmdName() - label := "" - if commandBuffer := getCommandBuffer(command); commandBuffer != "" { - if _, ok := builder.labelToHierarchy[commandBuffer]; !ok { - builder.labelToHierarchy[commandBuffer] = &api.Hierarchy{} + if commandBufferId, ok := getCommandBufferId(command); ok { + hierarchy, ok := builder.commandBufferIdToHierarchy[commandBufferId] + if !ok { + hierarchy = &api.Hierarchy{} + builder.commandBufferIdToHierarchy[commandBufferId] = hierarchy + builder.commandBufferIdToOrderNumber[commandBufferId] = len(builder.commandBufferIdToOrderNumber) + 1 } - hierarchy := builder.labelToHierarchy[commandBuffer] - label += commandBuffer + "/" - label += getLabelFromHierarchy(commandName, commandHierarchyNames, hierarchy) - label += fmt.Sprintf("%s_%d", commandName, commandNodeId) + label.PushBack(COMMAND_BUFFER, builder.commandBufferIdToOrderNumber[commandBufferId]) + label.PushBackLabel(getLabelFromHierarchy(commandName, commandHierarchyNames, hierarchy)) + label.PushBack(commandName, int(cmdId)) } else { - label += fmt.Sprintf("%s_%d", commandName, commandNodeId) + label.PushBack(commandName, int(cmdId)) } + checkDebugMarkers(builder, label) return label } -func (builder *labelForVulkanCommands) GetSubCommandLabel(index api.SubCmdIdx, commandName, subCommandName string) string { - label := commandName - subCommandIndexName := commandName +// GetSubCommandLabel returns the Label for the Vulkan subcommand. +func (builder *labelForVulkanCommands) GetSubCommandLabel(index api.SubCmdIdx, commandName string, cmdId uint64, subCommandName string) *api.Label { + label := &api.Label{} + label.PushBack(commandName, int(cmdId)) + var subCommandIndexName bytes.Buffer + fmt.Fprintf(&subCommandIndexName, "%s_%d", commandName, cmdId) for i := 1; i < len(index); i++ { - subCommandIndexName += fmt.Sprintf("/%d", index[i]) + fmt.Fprintf(&subCommandIndexName, "/%d", index[i]) if i+1 < len(index) { - if hierarchyLabel, ok := builder.subCommandIndexNameToHierarchyLabel[subCommandIndexName]; ok { - label += "/" + hierarchyLabel + if hierarchyLabel, ok := builder.subCommandIndexNameToHierarchyLabel[subCommandIndexName.String()]; ok { + label.PushBackLabel(hierarchyLabel) } else { - label += fmt.Sprintf("/%d", index[i]) + label.PushBack("", int(index[i])) } } } - if _, ok := builder.labelToHierarchy[label]; !ok { - builder.labelToHierarchy[label] = &api.Hierarchy{} + temporaryLabelAsAString := label.GetLabelAsAString() + hierarchy, ok := builder.labelAsAStringToHierarchy[temporaryLabelAsAString] + if !ok { + hierarchy = &api.Hierarchy{} + builder.labelAsAStringToHierarchy[temporaryLabelAsAString] = hierarchy } - hierarchy := builder.labelToHierarchy[label] labelFromHierarchy := getLabelFromHierarchy(subCommandName, subCommandHierarchyNames, hierarchy) - labelFromHierarchy += fmt.Sprintf("%s_%d", subCommandName, index[len(index)-1]) - builder.subCommandIndexNameToHierarchyLabel[subCommandIndexName] = labelFromHierarchy + labelFromHierarchy.PushBack(subCommandName, int(index[len(index)-1])) + builder.subCommandIndexNameToHierarchyLabel[subCommandIndexName.String()] = labelFromHierarchy - label += "/" + labelFromHierarchy + label.PushBackLabel(labelFromHierarchy) + checkDebugMarkers(builder, label) return label } -func getLabelFromHierarchy(name string, hierarchyNames *api.HierarchyNames, hierarchy *api.Hierarchy) string { +func getLabelFromHierarchy(name string, hierarchyNames *api.HierarchyNames, hierarchy *api.Hierarchy) *api.Label { isEndCommand := false if level, ok := hierarchyNames.BeginNameToLevel[name]; ok { hierarchy.PushBackToResize(level + 1) @@ -129,9 +209,9 @@ func getLabelFromHierarchy(name string, hierarchyNames *api.HierarchyNames, hier isEndCommand = true } - label := "" + label := &api.Label{} for level := 1; level < hierarchy.GetSize(); level++ { - label += fmt.Sprintf("%d_%s/", hierarchy.GetID(level), hierarchyNames.GetName(level)) + label.PushBack(hierarchyNames.GetName(level), hierarchy.GetID(level)) } if level, ok := hierarchyNames.BeginNameToLevel[name]; ok && name == VK_CMD_BEGIN_RENDER_PASS { @@ -145,9 +225,13 @@ func getLabelFromHierarchy(name string, hierarchyNames *api.HierarchyNames, hier return label } +// GetGraphVisualizationBuilder returns a builder to process commands from +// Vulkan graphics API in order to get the Label for commands. func (API) GetGraphVisualizationBuilder() api.GraphVisualizationBuilder { return &labelForVulkanCommands{ - labelToHierarchy: map[string]*api.Hierarchy{}, - subCommandIndexNameToHierarchyLabel: map[string]string{}, + labelAsAStringToHierarchy: map[string]*api.Hierarchy{}, + subCommandIndexNameToHierarchyLabel: map[string]*api.Label{}, + commandBufferIdToHierarchy: map[VkCommandBuffer]*api.Hierarchy{}, + commandBufferIdToOrderNumber: map[VkCommandBuffer]int{}, } } diff --git a/gapis/resolve/dependencygraph2/graph_visualization/BUILD.bazel b/gapis/resolve/dependencygraph2/graph_visualization/BUILD.bazel index dcceacf291..ee3d258764 100644 --- a/gapis/resolve/dependencygraph2/graph_visualization/BUILD.bazel +++ b/gapis/resolve/dependencygraph2/graph_visualization/BUILD.bazel @@ -17,6 +17,8 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", srcs = [ + "graph_algorithms.go", + "graph_output.go", "graph_structure.go", "graph_visualization.go", ], @@ -32,6 +34,10 @@ go_library( go_test( name = "go_default_test", - srcs = ["graph_structure_test.go"], + srcs = [ + "graph_algorithms_test.go", + "graph_structure_test.go", + ], embed = [":go_default_library"], + deps = ["//gapis/api:go_default_library"], ) diff --git a/gapis/resolve/dependencygraph2/graph_visualization/graph_algorithms.go b/gapis/resolve/dependencygraph2/graph_visualization/graph_algorithms.go new file mode 100644 index 0000000000..ff4affa85c --- /dev/null +++ b/gapis/resolve/dependencygraph2/graph_visualization/graph_algorithms.go @@ -0,0 +1,154 @@ +// Copyright (C) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package graph_visualization + +import ( + "github.com/google/gapid/gapis/api" +) + +const ( + NO_VISITED = 0 + VISITED_AND_USED = -1 + FRAME = "FRAME" + UNUSED = "UNUSED" +) + +// It is used to find the Strongly Connected Components (SCC) in a directed graph based on Tarjan algorithm +type tarjan struct { + visitTime []int + minVisitTime []int + idInSCC []int + visitedNodesId []int + currentId int + currentTime int +} + +func (g *graph) traverseGraphToFindSCC(currentNode *node, tarjanParameters *tarjan) { + tarjanParameters.visitedNodesId = append(tarjanParameters.visitedNodesId, currentNode.id) + tarjanParameters.visitTime[currentNode.id] = tarjanParameters.currentTime + tarjanParameters.minVisitTime[currentNode.id] = tarjanParameters.currentTime + tarjanParameters.currentTime++ + + for neighbourId := range currentNode.outNeighbourIdToEdgeId { + neighbour := g.nodeIdToNode[neighbourId] + if tarjanParameters.visitTime[neighbour.id] == NO_VISITED { + g.traverseGraphToFindSCC(neighbour, tarjanParameters) + } + if tarjanParameters.visitTime[neighbour.id] != VISITED_AND_USED { + if tarjanParameters.minVisitTime[neighbour.id] < tarjanParameters.minVisitTime[currentNode.id] { + tarjanParameters.minVisitTime[currentNode.id] = tarjanParameters.minVisitTime[neighbour.id] + } + } + } + + if tarjanParameters.minVisitTime[currentNode.id] == tarjanParameters.visitTime[currentNode.id] { + for { + lastNodeId := tarjanParameters.visitedNodesId[len(tarjanParameters.visitedNodesId)-1] + tarjanParameters.visitTime[lastNodeId] = VISITED_AND_USED + tarjanParameters.visitedNodesId = tarjanParameters.visitedNodesId[:len(tarjanParameters.visitedNodesId)-1] + tarjanParameters.idInSCC[lastNodeId] = tarjanParameters.currentId + if lastNodeId == currentNode.id { + break + } + } + tarjanParameters.currentId++ + } +} + +func (g *graph) getIdInStronglyConnectedComponents() []int { + + tarjanParameters := tarjan{ + visitTime: make([]int, g.maxNodeId+1), + minVisitTime: make([]int, g.maxNodeId+1), + idInSCC: make([]int, g.maxNodeId+1), + currentId: 1, + currentTime: 1, + } + + for _, currentNode := range g.nodeIdToNode { + if tarjanParameters.visitTime[currentNode.id] == NO_VISITED { + g.traverseGraphToFindSCC(currentNode, &tarjanParameters) + } + } + return tarjanParameters.idInSCC +} + +func (g *graph) makeStronglyConnectedComponentsByCommandTypeId() { + newGraph := createGraph(0) + for _, currentNode := range g.nodeIdToNode { + newNode := getNewNode(currentNode.commandTypeId, &api.Label{}) + newGraph.addNode(newNode) + } + + for _, currentNode := range g.nodeIdToNode { + for neighbourId := range currentNode.outNeighbourIdToEdgeId { + neighbour := g.nodeIdToNode[neighbourId] + newGraph.addEdgeBetweenNodesById(currentNode.commandTypeId, neighbour.commandTypeId) + } + } + idInStronglyConnectedComponents := newGraph.getIdInStronglyConnectedComponents() + for _, currentNode := range g.nodeIdToNode { + id := idInStronglyConnectedComponents[currentNode.commandTypeId] + currentNode.label.PushBack("SCC", id) + } +} + +func (g *graph) bfs(sourceNode *node, visited []bool, visitedNodes *[]*node) { + head := len(*visitedNodes) + visited[sourceNode.id] = true + *visitedNodes = append(*visitedNodes, sourceNode) + for head < len(*visitedNodes) { + currentNode := (*visitedNodes)[head] + head++ + neighbours := g.getSortedNeighbours(currentNode.outNeighbourIdToEdgeId) + for _, neighbour := range neighbours { + if !visited[neighbour.id] { + visited[neighbour.id] = true + *visitedNodes = append(*visitedNodes, neighbour) + } + } + + for _, subCommandNode := range currentNode.subCommandNodes { + if !visited[subCommandNode.id] { + visited[subCommandNode.id] = true + *visitedNodes = append(*visitedNodes, subCommandNode) + } + } + } +} + +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.bfs(currentNode, visited, &visitedNodes) + for _, visitedNode := range visitedNodes { + visitedNode.label.PushFront(FRAME, frameNumber) + } + frameNumber++ + } + } +} + +func (g *graph) joinNodesWithZeroDegree() { + for _, currentNode := range g.nodeIdToNode { + if (len(currentNode.inNeighbourIdToEdgeId) + len(currentNode.outNeighbourIdToEdgeId)) == 0 { + currentNode.label.PushFront(UNUSED, 0) + } + } +} diff --git a/gapis/resolve/dependencygraph2/graph_visualization/graph_algorithms_test.go b/gapis/resolve/dependencygraph2/graph_visualization/graph_algorithms_test.go new file mode 100644 index 0000000000..e55728158e --- /dev/null +++ b/gapis/resolve/dependencygraph2/graph_visualization/graph_algorithms_test.go @@ -0,0 +1,124 @@ +// Copyright (C) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package graph_visualization + +import ( + "testing" +) + +func TestGetIdInStronglyConnectedComponents(t *testing.T) { + + currentGraph := createGraph(0) + numberOfNodes := 12 + for id := 0; id < numberOfNodes; id++ { + addNodeByIdAndName(currentGraph, id, "") + } + currentGraph.addEdgeBetweenNodesById(0, 1) + currentGraph.addEdgeBetweenNodesById(1, 2) + currentGraph.addEdgeBetweenNodesById(2, 3) + currentGraph.addEdgeBetweenNodesById(3, 0) + currentGraph.addEdgeBetweenNodesById(3, 4) + currentGraph.addEdgeBetweenNodesById(3, 5) + currentGraph.addEdgeBetweenNodesById(3, 6) + currentGraph.addEdgeBetweenNodesById(4, 9) + currentGraph.addEdgeBetweenNodesById(4, 11) + currentGraph.addEdgeBetweenNodesById(11, 10) + currentGraph.addEdgeBetweenNodesById(11, 8) + currentGraph.addEdgeBetweenNodesById(6, 10) + currentGraph.addEdgeBetweenNodesById(6, 7) + currentGraph.addEdgeBetweenNodesById(7, 6) + currentGraph.addEdgeBetweenNodesById(10, 8) + currentGraph.addEdgeBetweenNodesById(8, 10) + currentGraph.addEdgeBetweenNodesById(7, 8) + currentGraph.addEdgeBetweenNodesById(8, 7) + + idInStronglyConnectedComponents := currentGraph.getIdInStronglyConnectedComponents() + wantedStronglyConnectedComponentes := [][]int{ + []int{0, 1, 2, 3}, + []int{6, 7, 8, 10}, + []int{4}, + []int{5}, + []int{9}, + []int{11}, + } + for _, currentScc := range wantedStronglyConnectedComponentes { + idInSccForNodes := map[int]bool{} + for _, idNode := range currentScc { + idInScc := idInStronglyConnectedComponents[idNode] + idInSccForNodes[idInScc] = true + } + if len(idInSccForNodes) != 1 { + t.Errorf("There are nodes belonging to different SCC %v", currentScc) + } + } + +} + +func TestBfs(t *testing.T) { + + currentGraph := createGraph(0) + numberOfNodes := 14 + for id := 0; id < numberOfNodes; id++ { + addNodeByIdAndName(currentGraph, id, "") + } + + currentGraph.addEdgeBetweenNodesById(0, 1) + currentGraph.addEdgeBetweenNodesById(0, 5) + currentGraph.addEdgeBetweenNodesById(1, 3) + currentGraph.addEdgeBetweenNodesById(1, 2) + currentGraph.addEdgeBetweenNodesById(5, 6) + currentGraph.addEdgeBetweenNodesById(3, 6) + currentGraph.addEdgeBetweenNodesById(2, 4) + currentGraph.addEdgeBetweenNodesById(4, 6) + currentGraph.addEdgeBetweenNodesById(7, 8) + currentGraph.addEdgeBetweenNodesById(7, 11) + currentGraph.addEdgeBetweenNodesById(11, 8) + currentGraph.addEdgeBetweenNodesById(9, 12) + currentGraph.addEdgeBetweenNodesById(12, 13) + + wantedNodeIdToNumberOfComponent := map[int]int{ + 0: 1, + 1: 1, + 2: 1, + 3: 1, + 4: 1, + 5: 1, + 6: 1, + 7: 2, + 8: 2, + 9: 3, + 10: 4, + 11: 2, + 12: 3, + 13: 3, + } + + visited := make([]bool, currentGraph.maxNodeId+1) + nodes := currentGraph.getSortedNodes() + for _, currentNode := range nodes { + if !visited[currentNode.id] { + visitedNodes := []*node{} + currentGraph.bfs(currentNode, visited, &visitedNodes) + numberComponent := map[int]bool{} + for _, visitedNode := range visitedNodes { + numberComponent[wantedNodeIdToNumberOfComponent[visitedNode.id]] = true + } + if len(numberComponent) != 1 { + t.Errorf("There are some nodes in different components") + } + } + } + +} diff --git a/gapis/resolve/dependencygraph2/graph_visualization/graph_output.go b/gapis/resolve/dependencygraph2/graph_visualization/graph_output.go new file mode 100644 index 0000000000..14eab759df --- /dev/null +++ b/gapis/resolve/dependencygraph2/graph_visualization/graph_output.go @@ -0,0 +1,66 @@ +// Copyright (C) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package graph_visualization + +import ( + "bytes" + "fmt" +) + +func (g *graph) writeEdgesInDotFormat(output *bytes.Buffer) { + nodes := g.getSortedNodes() + for _, currentNode := range nodes { + inNeighbours := g.getSortedNeighbours(currentNode.inNeighbourIdToEdgeId) + for _, neighbour := range inNeighbours { + fmt.Fprintf(output, "%d -> %d;\n", neighbour.id, currentNode.id) + } + } +} + +func (g *graph) writeNodesInDotFormat(output *bytes.Buffer) { + nodes := g.getSortedNodes() + for _, currentNode := range nodes { + fmt.Fprintf(output, "%d[label=%s];\n", currentNode.id, currentNode.label) + } +} + +func (g *graph) getGraphInDotFormat() []byte { + var output bytes.Buffer + output.WriteString("digraph g {\n") + g.writeNodesInDotFormat(&output) + g.writeEdgesInDotFormat(&output) + 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.GetLabelAsAString() + "\"\n") + fmt.Fprintf(&output, "op: \"%s%d\"\n", currentNode.label.GetCommandName(), currentNode.label.GetCommandId()) + + neighbours := g.getSortedNeighbours(currentNode.inNeighbourIdToEdgeId) + for _, neighbour := range neighbours { + output.WriteString("input: \"" + neighbour.label.GetLabelAsAString() + "\"\n") + } + output.WriteString("attr {\n") + output.WriteString("key: \"" + currentNode.attributes + "\"\n") + output.WriteString("}\n") + output.WriteString("}\n") + } + return output.Bytes() +} diff --git a/gapis/resolve/dependencygraph2/graph_visualization/graph_structure.go b/gapis/resolve/dependencygraph2/graph_visualization/graph_structure.go index 710f5b20fe..e38eedb31d 100644 --- a/gapis/resolve/dependencygraph2/graph_visualization/graph_structure.go +++ b/gapis/resolve/dependencygraph2/graph_visualization/graph_structure.go @@ -15,25 +15,17 @@ package graph_visualization import ( - "bytes" "fmt" + "github.com/google/gapid/gapis/api" "sort" ) -const ( - NO_VISITED = 0 - VISITED_AND_USED = -1 - FRAME = "FRAME" - UNUSED = "UNUSED" -) - type node struct { inNeighbourIdToEdgeId map[int]int outNeighbourIdToEdgeId map[int]int id int commandTypeId int - label string - name string + label *api.Label attributes string isEndOfFrame bool subCommandNodes []*node @@ -83,7 +75,7 @@ func (g *graph) addNode(newNode *node) error { return nil } -func getNewNode(id int, label string) *node { +func getNewNode(id int, label *api.Label) *node { newNode := &node{ inNeighbourIdToEdgeId: map[int]int{}, outNeighbourIdToEdgeId: map[int]int{}, @@ -197,122 +189,6 @@ func (input nodeSorter) Less(i, j int) bool { return input[i].id < input[j].id } -func (g *graph) traverseGraph(currentNode *node, visitTime, minVisitTime, idInStronglyConnectedComponents, visitedNodesId *[]int, currentId, currentTime *int) { - *visitedNodesId = append(*visitedNodesId, currentNode.id) - (*visitTime)[currentNode.id] = *currentTime - (*minVisitTime)[currentNode.id] = *currentTime - (*currentTime)++ - - for neighbourId := range currentNode.outNeighbourIdToEdgeId { - neighbour := g.nodeIdToNode[neighbourId] - if (*visitTime)[neighbour.id] == NO_VISITED { - g.traverseGraph(neighbour, visitTime, minVisitTime, idInStronglyConnectedComponents, visitedNodesId, currentId, currentTime) - } - if (*visitTime)[neighbour.id] != VISITED_AND_USED { - if (*minVisitTime)[neighbour.id] < (*minVisitTime)[currentNode.id] { - (*minVisitTime)[currentNode.id] = (*minVisitTime)[neighbour.id] - } - } - } - - if (*minVisitTime)[currentNode.id] == (*visitTime)[currentNode.id] { - for { - lastNodeId := (*visitedNodesId)[len(*visitedNodesId)-1] - (*visitTime)[lastNodeId] = VISITED_AND_USED - *visitedNodesId = (*visitedNodesId)[:len(*visitedNodesId)-1] - (*idInStronglyConnectedComponents)[lastNodeId] = *currentId - if lastNodeId == currentNode.id { - break - } - } - (*currentId)++ - } -} - -func (g *graph) getIdInStronglyConnectedComponents() []int { - currentId := 0 - currentTime := 1 - visitTime := make([]int, g.maxNodeId+1) - minVisitTime := make([]int, g.maxNodeId+1) - idInStronglyConnectedComponents := make([]int, g.maxNodeId+1) - visitedNodesId := make([]int, 0) - - for _, currentNode := range g.nodeIdToNode { - if visitTime[currentNode.id] == NO_VISITED { - g.traverseGraph(currentNode, &visitTime, &minVisitTime, &idInStronglyConnectedComponents, &visitedNodesId, ¤tId, ¤tTime) - } - } - return idInStronglyConnectedComponents -} - -func (g *graph) makeStronglyConnectedComponentsByCommandTypeId() { - newGraph := createGraph(0) - for _, currentNode := range g.nodeIdToNode { - newNode := getNewNode(currentNode.commandTypeId, "") - newGraph.addNode(newNode) - } - - for _, currentNode := range g.nodeIdToNode { - for neighbourId := range currentNode.outNeighbourIdToEdgeId { - neighbour := g.nodeIdToNode[neighbourId] - newGraph.addEdgeBetweenNodesById(currentNode.commandTypeId, neighbour.commandTypeId) - } - } - idInStronglyConnectedComponents := newGraph.getIdInStronglyConnectedComponents() - for _, currentNode := range g.nodeIdToNode { - id := idInStronglyConnectedComponents[currentNode.commandTypeId] - currentNode.label = currentNode.label + "/" + fmt.Sprintf("SCC%d", id) - } -} - -func (g *graph) bfs(sourceNode *node, visited []bool, visitedNodes *[]*node) { - head := len(*visitedNodes) - visited[sourceNode.id] = true - *visitedNodes = append(*visitedNodes, sourceNode) - for head < len(*visitedNodes) { - currentNode := (*visitedNodes)[head] - head++ - neighbours := g.getSortedNeighbours(currentNode.outNeighbourIdToEdgeId) - for _, neighbour := range neighbours { - if !visited[neighbour.id] { - visited[neighbour.id] = true - *visitedNodes = append(*visitedNodes, neighbour) - } - } - - for _, subCommandNode := range currentNode.subCommandNodes { - if !visited[subCommandNode.id] { - visited[subCommandNode.id] = true - *visitedNodes = append(*visitedNodes, subCommandNode) - } - } - } -} - -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.bfs(currentNode, visited, &visitedNodes) - for _, visitedNode := range visitedNodes { - visitedNode.label = fmt.Sprintf("%s%d/%s", FRAME, frameNumber, visitedNode.label) - } - frameNumber++ - } - } -} - -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) getSortedNodes() []*node { nodes := []*node{} for _, currentNode := range g.nodeIdToNode { @@ -330,49 +206,3 @@ func (g *graph) getSortedNeighbours(neighbourIdToEdgeId map[int]int) []*node { sort.Sort(nodeSorter(neighbours)) return neighbours } - -func (g *graph) writeEdgesInDotFormat(output *bytes.Buffer) { - nodes := g.getSortedNodes() - for _, currentNode := range nodes { - inNeighbours := g.getSortedNeighbours(currentNode.inNeighbourIdToEdgeId) - for _, neighbour := range inNeighbours { - fmt.Fprintf(output, "%d -> %d;\n", neighbour.id, currentNode.id) - } - } -} - -func (g *graph) writeNodesInDotFormat(output *bytes.Buffer) { - nodes := g.getSortedNodes() - for _, currentNode := range nodes { - fmt.Fprintf(output, "%d[label=%s];\n", currentNode.id, currentNode.label) - } -} - -func (g *graph) getGraphInDotFormat() []byte { - var output bytes.Buffer - output.WriteString("digraph g {\n") - g.writeNodesInDotFormat(&output) - g.writeEdgesInDotFormat(&output) - 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 { - output.WriteString("input: \"" + neighbour.label + "\"\n") - } - output.WriteString("attr {\n") - output.WriteString("key: \"" + currentNode.attributes + "\"\n") - output.WriteString("}\n") - output.WriteString("}\n") - } - return output.Bytes() -} diff --git a/gapis/resolve/dependencygraph2/graph_visualization/graph_structure_test.go b/gapis/resolve/dependencygraph2/graph_visualization/graph_structure_test.go index 71108d5afc..379ce1da9f 100644 --- a/gapis/resolve/dependencygraph2/graph_visualization/graph_structure_test.go +++ b/gapis/resolve/dependencygraph2/graph_visualization/graph_structure_test.go @@ -15,6 +15,7 @@ package graph_visualization import ( + "github.com/google/gapid/gapis/api" "reflect" "sort" "testing" @@ -38,14 +39,15 @@ func getSortedKeysForNodes(input map[int]*node) []int { return sortedKeys } -func addNodeByIdAndLabel(g *graph, id int, label string) { +func addNodeByIdAndName(g *graph, id int, name string) { + label := &api.Label{LevelsName: []string{name}} newNode := getNewNode(id, label) g.addNode(newNode) } -func addNodeByIdAndLabelAndNameAndAttributes(g *graph, id int, label, name, attributes string) { +func addNodeByIdAndNameAndAttributes(g *graph, id int, name, attributes string) { + label := &api.Label{LevelsName: []string{name}} newNode := getNewNode(id, label) - newNode.name = name newNode.attributes = attributes g.addNode(newNode) } @@ -71,14 +73,10 @@ func areEqualGraphs(t *testing.T, wantedGraph *graph, obtainedGraph *graph) bool for _, id := range wantedSortedIdNodes { wantedNode := wantedGraph.nodeIdToNode[id] obtainedNode := obtainedGraph.nodeIdToNode[id] - if wantedNode.label != obtainedNode.label { + if !reflect.DeepEqual(wantedNode.label, obtainedNode.label) { t.Errorf("The labels from nodes with ID %d are different %v != %v\n", id, wantedNode.label, obtainedNode.label) return false } - if wantedNode.name != obtainedNode.name { - t.Errorf("The names from nodes with ID %d are different %v != %v\n", id, wantedNode.name, obtainedNode.name) - return false - } wantedInNeighbourIdSorted := getSortedKeys(wantedNode.inNeighbourIdToEdgeId) obtainedInNeighbourIdSorted := getSortedKeys(obtainedNode.inNeighbourIdToEdgeId) if !reflect.DeepEqual(wantedInNeighbourIdSorted, obtainedInNeighbourIdSorted) { @@ -98,32 +96,32 @@ func areEqualGraphs(t *testing.T, wantedGraph *graph, obtainedGraph *graph) bool func TestGraph1(t *testing.T) { wantedGraph := createGraph(0) - addNodeByIdAndLabel(wantedGraph, 0, "A") - addNodeByIdAndLabel(wantedGraph, 1, "B") - addNodeByIdAndLabel(wantedGraph, 2, "C") - addNodeByIdAndLabel(wantedGraph, 3, "D") - addNodeByIdAndLabel(wantedGraph, 4, "E") - addNodeByIdAndLabel(wantedGraph, 5, "F") - addNodeByIdAndLabel(wantedGraph, 6, "G") - addNodeByIdAndLabel(wantedGraph, 7, "H") - addNodeByIdAndLabel(wantedGraph, 8, "I") - addNodeByIdAndLabel(wantedGraph, 9, "J") + addNodeByIdAndName(wantedGraph, 0, "A") + addNodeByIdAndName(wantedGraph, 1, "B") + addNodeByIdAndName(wantedGraph, 2, "C") + addNodeByIdAndName(wantedGraph, 3, "D") + addNodeByIdAndName(wantedGraph, 4, "E") + addNodeByIdAndName(wantedGraph, 5, "F") + addNodeByIdAndName(wantedGraph, 6, "G") + addNodeByIdAndName(wantedGraph, 7, "H") + addNodeByIdAndName(wantedGraph, 8, "I") + addNodeByIdAndName(wantedGraph, 9, "J") obtainedGraph := createGraph(0) - addNodeByIdAndLabel(obtainedGraph, 0, "A") - addNodeByIdAndLabel(obtainedGraph, 1, "B") - addNodeByIdAndLabel(obtainedGraph, 2, "C") - addNodeByIdAndLabel(obtainedGraph, 3, "D") - addNodeByIdAndLabel(obtainedGraph, 4, "E") - addNodeByIdAndLabel(obtainedGraph, 5, "F") - addNodeByIdAndLabel(obtainedGraph, 6, "G") - addNodeByIdAndLabel(obtainedGraph, 7, "H") - addNodeByIdAndLabel(obtainedGraph, 8, "I") - addNodeByIdAndLabel(obtainedGraph, 9, "J") - - addNodeByIdAndLabel(obtainedGraph, 10, "K") - addNodeByIdAndLabel(obtainedGraph, 11, "L") - addNodeByIdAndLabel(obtainedGraph, 12, "M") + addNodeByIdAndName(obtainedGraph, 0, "A") + addNodeByIdAndName(obtainedGraph, 1, "B") + addNodeByIdAndName(obtainedGraph, 2, "C") + addNodeByIdAndName(obtainedGraph, 3, "D") + addNodeByIdAndName(obtainedGraph, 4, "E") + addNodeByIdAndName(obtainedGraph, 5, "F") + addNodeByIdAndName(obtainedGraph, 6, "G") + addNodeByIdAndName(obtainedGraph, 7, "H") + addNodeByIdAndName(obtainedGraph, 8, "I") + addNodeByIdAndName(obtainedGraph, 9, "J") + + addNodeByIdAndName(obtainedGraph, 10, "K") + addNodeByIdAndName(obtainedGraph, 11, "L") + addNodeByIdAndName(obtainedGraph, 12, "M") obtainedGraph.removeNodeById(10) obtainedGraph.removeNodeById(11) obtainedGraph.removeNodeById(12) @@ -132,9 +130,9 @@ func TestGraph1(t *testing.T) { t.Errorf("The graphs are different\n") } - addNodeByIdAndLabel(obtainedGraph, 10, "K") - addNodeByIdAndLabel(obtainedGraph, 11, "L") - addNodeByIdAndLabel(obtainedGraph, 12, "M") + addNodeByIdAndName(obtainedGraph, 10, "K") + addNodeByIdAndName(obtainedGraph, 11, "L") + addNodeByIdAndName(obtainedGraph, 12, "M") obtainedGraph.addEdgeBetweenNodesById(10, 1) obtainedGraph.addEdgeBetweenNodesById(10, 11) obtainedGraph.addEdgeBetweenNodesById(10, 4) @@ -156,26 +154,26 @@ func TestGraph2(t *testing.T) { wantedGraph := createGraph(0) obtainedGraph := createGraph(0) - addNodeByIdAndLabelAndNameAndAttributes(obtainedGraph, 0, "A", "vkCommandBuffer0", "") - addNodeByIdAndLabelAndNameAndAttributes(obtainedGraph, 1, "A", "vkCommandBuffer1", "") - addNodeByIdAndLabelAndNameAndAttributes(obtainedGraph, 2, "A", "vkCommandBuffer2", "") - addNodeByIdAndLabelAndNameAndAttributes(obtainedGraph, 3, "A", "vkCommandBuffer3", "") - addNodeByIdAndLabelAndNameAndAttributes(obtainedGraph, 4, "A", "vkCommandBuffer4", "") - addNodeByIdAndLabelAndNameAndAttributes(obtainedGraph, 5, "A", "vkCommandBuffer5", "") - addNodeByIdAndLabelAndNameAndAttributes(obtainedGraph, 6, "A", "vkCommandBuffer6", "") - addNodeByIdAndLabelAndNameAndAttributes(obtainedGraph, 7, "A", "vkCommandBuffer7", "") + addNodeByIdAndNameAndAttributes(obtainedGraph, 0, "vkCommandBuffer0", "") + addNodeByIdAndNameAndAttributes(obtainedGraph, 1, "vkCommandBuffer1", "") + addNodeByIdAndNameAndAttributes(obtainedGraph, 2, "vkCommandBuffer2", "") + addNodeByIdAndNameAndAttributes(obtainedGraph, 3, "vkCommandBuffer3", "") + addNodeByIdAndNameAndAttributes(obtainedGraph, 4, "vkCommandBuffer4", "") + addNodeByIdAndNameAndAttributes(obtainedGraph, 5, "vkCommandBuffer5", "") + addNodeByIdAndNameAndAttributes(obtainedGraph, 6, "vkCommandBuffer6", "") + addNodeByIdAndNameAndAttributes(obtainedGraph, 7, "vkCommandBuffer7", "") obtainedGraph.removeNodesWithZeroDegree() if !areEqualGraphs(t, wantedGraph, obtainedGraph) { t.Errorf("The graphs are different\n") } - addNodeByIdAndLabelAndNameAndAttributes(wantedGraph, 0, "A", "vkCommandBuffer0", "") - addNodeByIdAndLabelAndNameAndAttributes(wantedGraph, 2, "B", "vkCommandBuffer2", "") - addNodeByIdAndLabelAndNameAndAttributes(wantedGraph, 6, "C", "vkCommandBuffer6", "") - addNodeByIdAndLabelAndNameAndAttributes(wantedGraph, 3, "D", "vkCommandBuffer3", "") - addNodeByIdAndLabelAndNameAndAttributes(wantedGraph, 4, "E", "vkCommandBuffer4", "") - addNodeByIdAndLabelAndNameAndAttributes(wantedGraph, 5, "F", "vkCommandBuffer5", "") + addNodeByIdAndNameAndAttributes(wantedGraph, 0, "vkCommandBuffer0", "") + addNodeByIdAndNameAndAttributes(wantedGraph, 2, "vkCommandBuffer2", "") + addNodeByIdAndNameAndAttributes(wantedGraph, 6, "vkCommandBuffer6", "") + addNodeByIdAndNameAndAttributes(wantedGraph, 3, "vkCommandBuffer3", "") + addNodeByIdAndNameAndAttributes(wantedGraph, 4, "vkCommandBuffer4", "") + addNodeByIdAndNameAndAttributes(wantedGraph, 5, "vkCommandBuffer5", "") wantedGraph.addEdgeBetweenNodesById(0, 3) wantedGraph.addEdgeBetweenNodesById(0, 4) wantedGraph.addEdgeBetweenNodesById(0, 5) @@ -186,13 +184,13 @@ func TestGraph2(t *testing.T) { wantedGraph.addEdgeBetweenNodesById(6, 4) wantedGraph.addEdgeBetweenNodesById(6, 5) - addNodeByIdAndLabelAndNameAndAttributes(obtainedGraph, 0, "A", "vkCommandBuffer0", "") - addNodeByIdAndLabelAndNameAndAttributes(obtainedGraph, 2, "B", "vkCommandBuffer2", "") - addNodeByIdAndLabelAndNameAndAttributes(obtainedGraph, 6, "C", "vkCommandBuffer6", "") - addNodeByIdAndLabelAndNameAndAttributes(obtainedGraph, 3, "D", "vkCommandBuffer3", "") - addNodeByIdAndLabelAndNameAndAttributes(obtainedGraph, 4, "E", "vkCommandBuffer4", "") - addNodeByIdAndLabelAndNameAndAttributes(obtainedGraph, 5, "F", "vkCommandBuffer5", "") - addNodeByIdAndLabelAndNameAndAttributes(obtainedGraph, 1, "G", "vkCommandBuffer1", "") + addNodeByIdAndNameAndAttributes(obtainedGraph, 0, "vkCommandBuffer0", "") + addNodeByIdAndNameAndAttributes(obtainedGraph, 2, "vkCommandBuffer2", "") + addNodeByIdAndNameAndAttributes(obtainedGraph, 6, "vkCommandBuffer6", "") + addNodeByIdAndNameAndAttributes(obtainedGraph, 3, "vkCommandBuffer3", "") + addNodeByIdAndNameAndAttributes(obtainedGraph, 4, "vkCommandBuffer4", "") + addNodeByIdAndNameAndAttributes(obtainedGraph, 5, "vkCommandBuffer5", "") + addNodeByIdAndNameAndAttributes(obtainedGraph, 1, "vkCommandBuffer1", "") obtainedGraph.addEdgeBetweenNodesById(0, 1) obtainedGraph.addEdgeBetweenNodesById(2, 1) obtainedGraph.addEdgeBetweenNodesById(6, 1) @@ -220,12 +218,12 @@ func TestGraph3(t *testing.T) { t.Errorf("The graphs are different\n") } - addNodeByIdAndLabel(wantedGraph, 123456, "") - addNodeByIdAndLabel(wantedGraph, 10, "") + addNodeByIdAndName(wantedGraph, 123456, "") + addNodeByIdAndName(wantedGraph, 10, "") wantedGraph.addEdgeBetweenNodesById(123456, 10) - addNodeByIdAndLabel(obtainedGraph, 123456, "") - addNodeByIdAndLabel(obtainedGraph, 10, "") + addNodeByIdAndName(obtainedGraph, 123456, "") + addNodeByIdAndName(obtainedGraph, 10, "") obtainedGraph.addEdgeBetweenNodesById(10, 123456) obtainedGraph.removeEdgeById(1) obtainedGraph.addEdgeBetweenNodesById(123456, 10) @@ -234,51 +232,3 @@ func TestGraph3(t *testing.T) { t.Errorf("The graphs are different\n") } } - -func TestGetIdInStronglyConnectedComponents(t *testing.T) { - - currentGraph := createGraph(0) - numberOfNodes := 12 - for id := 0; id < numberOfNodes; id++ { - addNodeByIdAndLabel(currentGraph, id, "") - } - currentGraph.addEdgeBetweenNodesById(0, 1) - currentGraph.addEdgeBetweenNodesById(1, 2) - currentGraph.addEdgeBetweenNodesById(2, 3) - currentGraph.addEdgeBetweenNodesById(3, 0) - currentGraph.addEdgeBetweenNodesById(3, 4) - currentGraph.addEdgeBetweenNodesById(3, 5) - currentGraph.addEdgeBetweenNodesById(3, 6) - currentGraph.addEdgeBetweenNodesById(4, 9) - currentGraph.addEdgeBetweenNodesById(4, 11) - currentGraph.addEdgeBetweenNodesById(11, 10) - currentGraph.addEdgeBetweenNodesById(11, 8) - currentGraph.addEdgeBetweenNodesById(6, 10) - currentGraph.addEdgeBetweenNodesById(6, 7) - currentGraph.addEdgeBetweenNodesById(7, 6) - currentGraph.addEdgeBetweenNodesById(10, 8) - currentGraph.addEdgeBetweenNodesById(8, 10) - currentGraph.addEdgeBetweenNodesById(7, 8) - currentGraph.addEdgeBetweenNodesById(8, 7) - - idInStronglyConnectedComponents := currentGraph.getIdInStronglyConnectedComponents() - wantedStronglyConnectedComponentes := [][]int{ - []int{0, 1, 2, 3}, - []int{6, 7, 8, 10}, - []int{4}, - []int{5}, - []int{9}, - []int{11}, - } - for _, currentScc := range wantedStronglyConnectedComponentes { - idInSccForNodes := map[int]bool{} - for _, idNode := range currentScc { - idInScc := idInStronglyConnectedComponents[idNode] - idInSccForNodes[idInScc] = true - } - if len(idInSccForNodes) != 1 { - t.Errorf("There are nodes belonging to different SCC %v", currentScc) - } - } - -} diff --git a/gapis/resolve/dependencygraph2/graph_visualization/graph_visualization.go b/gapis/resolve/dependencygraph2/graph_visualization/graph_visualization.go index feffd1c066..26ac05d2ed 100644 --- a/gapis/resolve/dependencygraph2/graph_visualization/graph_visualization.go +++ b/gapis/resolve/dependencygraph2/graph_visualization/graph_visualization.go @@ -53,10 +53,9 @@ func createGraphFromDependencyGraph(ctx context.Context, dependencyGraph depende commandNameAndId := fmt.Sprintf("%s_%d", command.CmdName(), cmdId) isSubCommand, parentNodeId := false, 0 - label, name := "", "" + label := &api.Label{} if len(cmdNode.Index) == 1 { label = builder.GetCommandLabel(command, cmdId) - name = command.CmdName() commandNameAndIdToNodeId[commandNameAndId] = int(nodeId) } else if len(cmdNode.Index) > 1 { dependencyNodeAccesses := dependencyGraph.GetNodeAccesses(nodeId) @@ -77,8 +76,7 @@ func createGraphFromDependencyGraph(ctx context.Context, dependencyGraph depende subCommandName = subCmd.CmdName() } } - label = builder.GetSubCommandLabel(cmdNode.Index, commandNameAndId, subCommandName) - name = subCommandName + label = builder.GetSubCommandLabel(cmdNode.Index, command.CmdName(), cmdId, subCommandName) } attributes := "" @@ -87,7 +85,6 @@ func createGraphFromDependencyGraph(ctx context.Context, dependencyGraph depende } newNode := getNewNode(int(nodeId), label) - newNode.name = name newNode.attributes = attributes newNode.isEndOfFrame = cmdNode.CmdFlags.IsEndOfFrame() if isSubCommand {