Vulkan: Implement debug markers

Covers both GL_KHR_debug and GL_EXT_debug_marker.

Debug markers are used to specify events or hierarchically categorize a
set of commands within the command buffer.  When debugging, this allows
for quicker navigation to the draw calls of interest, and otherwise
provides context to debug output.

Bug: angleproject:2853
Change-Id: Id65e11fc877d9e70b6fd0fae7f0bbbcb1164bf10
Reviewed-on: https://chromium-review.googlesource.com/c/1403956
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/CommandGraph.cpp b/src/libANGLE/renderer/vulkan/CommandGraph.cpp
index 2aef407..5f6892b 100644
--- a/src/libANGLE/renderer/vulkan/CommandGraph.cpp
+++ b/src/libANGLE/renderer/vulkan/CommandGraph.cpp
@@ -85,11 +85,45 @@
                     UNREACHABLE();
                     return "FenceSync";
             }
+        case CommandGraphResourceType::DebugMarker:
+            switch (function)
+            {
+                case CommandGraphNodeFunction::InsertDebugMarker:
+                    return "InsertDebugMarker";
+                case CommandGraphNodeFunction::PushDebugMarker:
+                    return "PushDebugMarker";
+                case CommandGraphNodeFunction::PopDebugMarker:
+                    return "PopDebugMarker";
+                default:
+                    UNREACHABLE();
+                    return "DebugMarker";
+            }
         default:
             UNREACHABLE();
             return "";
     }
 }
+
+void MakeDebugUtilsLabel(GLenum source, const char *marker, VkDebugUtilsLabelEXT *label)
+{
+    static constexpr angle::ColorF kLabelColors[6] = {
+        angle::ColorF(1.0f, 0.5f, 0.5f, 1.0f),  // DEBUG_SOURCE_API
+        angle::ColorF(0.5f, 1.0f, 0.5f, 1.0f),  // DEBUG_SOURCE_WINDOW_SYSTEM
+        angle::ColorF(0.5f, 0.5f, 1.0f, 1.0f),  // DEBUG_SOURCE_SHADER_COMPILER
+        angle::ColorF(0.7f, 0.7f, 0.7f, 1.0f),  // DEBUG_SOURCE_THIRD_PARTY
+        angle::ColorF(0.5f, 0.8f, 0.9f, 1.0f),  // DEBUG_SOURCE_APPLICATION
+        angle::ColorF(0.9f, 0.8f, 0.5f, 1.0f),  // DEBUG_SOURCE_OTHER
+    };
+
+    int colorIndex = source - GL_DEBUG_SOURCE_API;
+    ASSERT(colorIndex >= 0 && static_cast<size_t>(colorIndex) < ArraySize(kLabelColors));
+
+    label->sType      = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
+    label->pNext      = nullptr;
+    label->pLabelName = marker;
+    kLabelColors[colorIndex].writeData(label->color);
+}
+
 }  // anonymous namespace
 
 // CommandGraphResource implementation.
@@ -356,6 +390,14 @@
     mFenceSyncEvent = event.getHandle();
 }
 
+void CommandGraphNode::setDebugMarker(GLenum source, std::string &&marker)
+{
+    ASSERT(mFunction == CommandGraphNodeFunction::InsertDebugMarker ||
+           mFunction == CommandGraphNodeFunction::PushDebugMarker);
+    mDebugMarkerSource = source;
+    mDebugMarker       = std::move(marker);
+}
+
 // Do not call this in anything but testing code, since it's slow.
 bool CommandGraphNode::isChildOf(CommandGraphNode *parent)
 {
@@ -499,6 +541,39 @@
 
             break;
 
+        case CommandGraphNodeFunction::InsertDebugMarker:
+            ASSERT(!mOutsideRenderPassCommands.valid() && !mInsideRenderPassCommands.valid());
+
+            if (vkCmdInsertDebugUtilsLabelEXT)
+            {
+                VkDebugUtilsLabelEXT label;
+                MakeDebugUtilsLabel(mDebugMarkerSource, mDebugMarker.c_str(), &label);
+
+                vkCmdInsertDebugUtilsLabelEXT(primaryCommandBuffer->getHandle(), &label);
+            }
+            break;
+
+        case CommandGraphNodeFunction::PushDebugMarker:
+            ASSERT(!mOutsideRenderPassCommands.valid() && !mInsideRenderPassCommands.valid());
+
+            if (vkCmdBeginDebugUtilsLabelEXT)
+            {
+                VkDebugUtilsLabelEXT label;
+                MakeDebugUtilsLabel(mDebugMarkerSource, mDebugMarker.c_str(), &label);
+
+                vkCmdBeginDebugUtilsLabelEXT(primaryCommandBuffer->getHandle(), &label);
+            }
+            break;
+
+        case CommandGraphNodeFunction::PopDebugMarker:
+            ASSERT(!mOutsideRenderPassCommands.valid() && !mInsideRenderPassCommands.valid());
+
+            if (vkCmdEndDebugUtilsLabelEXT)
+            {
+                vkCmdEndDebugUtilsLabelEXT(primaryCommandBuffer->getHandle());
+            }
+            break;
+
         default:
             UNREACHABLE();
     }
@@ -712,6 +787,26 @@
     newNode->setFenceSync(event);
 }
 
+void CommandGraph::insertDebugMarker(GLenum source, std::string &&marker)
+{
+    CommandGraphNode *newNode = allocateBarrierNode(CommandGraphResourceType::DebugMarker,
+                                                    CommandGraphNodeFunction::InsertDebugMarker);
+    newNode->setDebugMarker(source, std::move(marker));
+}
+
+void CommandGraph::pushDebugMarker(GLenum source, std::string &&marker)
+{
+    CommandGraphNode *newNode = allocateBarrierNode(CommandGraphResourceType::DebugMarker,
+                                                    CommandGraphNodeFunction::PushDebugMarker);
+    newNode->setDebugMarker(source, std::move(marker));
+}
+
+void CommandGraph::popDebugMarker()
+{
+    allocateBarrierNode(CommandGraphResourceType::DebugMarker,
+                        CommandGraphNodeFunction::PopDebugMarker);
+}
+
 // Dumps the command graph into a dot file that works with graphviz.
 void CommandGraph::dumpGraphDotFile(std::ostream &out) const
 {
@@ -739,38 +834,53 @@
 
         std::stringstream strstr;
         strstr << GetResourceTypeName(node->getResourceTypeForDiagnostics(), node->getFunction());
-        strstr << " ";
 
-        auto it = objectIDMap.find(node->getResourceIDForDiagnostics());
-        if (it != objectIDMap.end())
+        if (node->getResourceTypeForDiagnostics() == CommandGraphResourceType::DebugMarker)
         {
-            strstr << it->second;
+            // For debug markers, use the string from the debug marker itself.
+            if (node->getFunction() != CommandGraphNodeFunction::PopDebugMarker)
+            {
+                strstr << " " << node->getDebugMarker();
+            }
         }
         else
         {
-            int id = 0;
+            strstr << " ";
 
-            switch (node->getResourceTypeForDiagnostics())
+            // Otherwise assign each object an ID, so all the nodes of the same object have the same
+            // label.
+            ASSERT(node->getResourceIDForDiagnostics() != 0);
+            auto it = objectIDMap.find(node->getResourceIDForDiagnostics());
+            if (it != objectIDMap.end())
             {
-                case CommandGraphResourceType::Buffer:
-                    id = bufferIDCounter++;
-                    break;
-                case CommandGraphResourceType::Framebuffer:
-                    id = framebufferIDCounter++;
-                    break;
-                case CommandGraphResourceType::Image:
-                    id = imageIDCounter++;
-                    break;
-                case CommandGraphResourceType::Query:
-                    id = queryIDCounter++;
-                    break;
-                default:
-                    UNREACHABLE();
-                    break;
+                strstr << it->second;
             }
+            else
+            {
+                int id = 0;
 
-            objectIDMap[node->getResourceIDForDiagnostics()] = id;
-            strstr << id;
+                switch (node->getResourceTypeForDiagnostics())
+                {
+                    case CommandGraphResourceType::Buffer:
+                        id = bufferIDCounter++;
+                        break;
+                    case CommandGraphResourceType::Framebuffer:
+                        id = framebufferIDCounter++;
+                        break;
+                    case CommandGraphResourceType::Image:
+                        id = imageIDCounter++;
+                        break;
+                    case CommandGraphResourceType::Query:
+                        id = queryIDCounter++;
+                        break;
+                    default:
+                        UNREACHABLE();
+                        break;
+                }
+
+                objectIDMap[node->getResourceIDForDiagnostics()] = id;
+                strstr << id;
+            }
         }
 
         const std::string &label = strstr.str();