Vulkan: Add CommandGraph class.

This also renames CommandBufferNode to CommandGraphNode. It also
renames some of the intenal members to more closely represent the
tree relationships (parents/children). This should clean up the
command graph classes and make them a bit easier to understand.

Bug: angleproject:2361
Change-Id: I024bffcc7f4157c78072ef902a3c40a07a08b18a
Reviewed-on: https://chromium-review.googlesource.com/922121
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/CommandGraph.cpp b/src/libANGLE/renderer/vulkan/CommandGraph.cpp
new file mode 100644
index 0000000..9643c1f
--- /dev/null
+++ b/src/libANGLE/renderer/vulkan/CommandGraph.cpp
@@ -0,0 +1,375 @@
+//
+// Copyright 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// CommandGraph:
+//    Deferred work constructed by GL calls, that will later be flushed to Vulkan.
+//
+
+#include "libANGLE/renderer/vulkan/CommandGraph.h"
+
+#include "libANGLE/renderer/vulkan/RenderTargetVk.h"
+#include "libANGLE/renderer/vulkan/RendererVk.h"
+#include "libANGLE/renderer/vulkan/vk_format_utils.h"
+
+namespace rx
+{
+
+namespace vk
+{
+
+namespace
+{
+
+Error InitAndBeginCommandBuffer(VkDevice device,
+                                const CommandPool &commandPool,
+                                const VkCommandBufferInheritanceInfo &inheritanceInfo,
+                                VkCommandBufferUsageFlags flags,
+                                CommandBuffer *commandBuffer)
+{
+    ASSERT(!commandBuffer->valid());
+
+    VkCommandBufferAllocateInfo createInfo;
+    createInfo.sType              = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+    createInfo.pNext              = nullptr;
+    createInfo.commandPool        = commandPool.getHandle();
+    createInfo.level              = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
+    createInfo.commandBufferCount = 1;
+
+    ANGLE_TRY(commandBuffer->init(device, createInfo));
+
+    VkCommandBufferBeginInfo beginInfo;
+    beginInfo.sType            = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+    beginInfo.pNext            = nullptr;
+    beginInfo.flags            = flags | VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+    beginInfo.pInheritanceInfo = &inheritanceInfo;
+
+    ANGLE_TRY(commandBuffer->begin(beginInfo));
+    return NoError();
+}
+
+}  // anonymous namespace
+
+// CommandGraphNode implementation.
+
+CommandGraphNode::CommandGraphNode() : mHasChildren(false), mVisitedState(VisitedState::Unvisited)
+{
+}
+
+CommandGraphNode::~CommandGraphNode()
+{
+    mRenderPassFramebuffer.setHandle(VK_NULL_HANDLE);
+
+    // Command buffers are managed by the command pool, so don't need to be freed.
+    mOutsideRenderPassCommands.releaseHandle();
+    mInsideRenderPassCommands.releaseHandle();
+}
+
+CommandBuffer *CommandGraphNode::getOutsideRenderPassCommands()
+{
+    ASSERT(!mHasChildren);
+    return &mOutsideRenderPassCommands;
+}
+
+CommandBuffer *CommandGraphNode::getInsideRenderPassCommands()
+{
+    ASSERT(!mHasChildren);
+    return &mInsideRenderPassCommands;
+}
+
+Error CommandGraphNode::beginOutsideRenderPassRecording(VkDevice device,
+                                                        const CommandPool &commandPool,
+                                                        CommandBuffer **commandsOut)
+{
+    ASSERT(!mHasChildren);
+
+    VkCommandBufferInheritanceInfo inheritanceInfo;
+    inheritanceInfo.sType                = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
+    inheritanceInfo.pNext                = nullptr;
+    inheritanceInfo.renderPass           = VK_NULL_HANDLE;
+    inheritanceInfo.subpass              = 0;
+    inheritanceInfo.framebuffer          = VK_NULL_HANDLE;
+    inheritanceInfo.occlusionQueryEnable = VK_FALSE;
+    inheritanceInfo.queryFlags           = 0;
+    inheritanceInfo.pipelineStatistics   = 0;
+
+    ANGLE_TRY(InitAndBeginCommandBuffer(device, commandPool, inheritanceInfo, 0,
+                                        &mOutsideRenderPassCommands));
+
+    *commandsOut = &mOutsideRenderPassCommands;
+    return NoError();
+}
+
+Error CommandGraphNode::beginInsideRenderPassRecording(RendererVk *renderer,
+                                                       CommandBuffer **commandsOut)
+{
+    ASSERT(!mHasChildren);
+
+    // Get a compatible RenderPass from the cache so we can initialize the inheritance info.
+    // TODO(jmadill): Support query for compatible/conformant render pass. htto://anglebug.com/2361
+    RenderPass *compatibleRenderPass;
+    ANGLE_TRY(renderer->getCompatibleRenderPass(mRenderPassDesc, &compatibleRenderPass));
+
+    VkCommandBufferInheritanceInfo inheritanceInfo;
+    inheritanceInfo.sType                = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
+    inheritanceInfo.pNext                = nullptr;
+    inheritanceInfo.renderPass           = compatibleRenderPass->getHandle();
+    inheritanceInfo.subpass              = 0;
+    inheritanceInfo.framebuffer          = mRenderPassFramebuffer.getHandle();
+    inheritanceInfo.occlusionQueryEnable = VK_FALSE;
+    inheritanceInfo.queryFlags           = 0;
+    inheritanceInfo.pipelineStatistics   = 0;
+
+    ANGLE_TRY(InitAndBeginCommandBuffer(
+        renderer->getDevice(), renderer->getCommandPool(), inheritanceInfo,
+        VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT, &mInsideRenderPassCommands));
+
+    *commandsOut = &mInsideRenderPassCommands;
+    return NoError();
+}
+
+void CommandGraphNode::storeRenderPassInfo(const Framebuffer &framebuffer,
+                                           const gl::Rectangle renderArea,
+                                           const std::vector<VkClearValue> &clearValues)
+{
+    mRenderPassFramebuffer.setHandle(framebuffer.getHandle());
+    mRenderPassRenderArea = renderArea;
+    std::copy(clearValues.begin(), clearValues.end(), mRenderPassClearValues.begin());
+}
+
+void CommandGraphNode::appendColorRenderTarget(Serial serial, RenderTargetVk *colorRenderTarget)
+{
+    // TODO(jmadill): Layout transition?
+    mRenderPassDesc.packColorAttachment(*colorRenderTarget->format, colorRenderTarget->samples);
+    colorRenderTarget->resource->onWriteResource(this, serial);
+}
+
+void CommandGraphNode::appendDepthStencilRenderTarget(Serial serial,
+                                                      RenderTargetVk *depthStencilRenderTarget)
+{
+    // TODO(jmadill): Layout transition?
+    mRenderPassDesc.packDepthStencilAttachment(*depthStencilRenderTarget->format,
+                                               depthStencilRenderTarget->samples);
+    depthStencilRenderTarget->resource->onWriteResource(this, serial);
+}
+
+// static
+void CommandGraphNode::SetHappensBeforeDependency(CommandGraphNode *beforeNode,
+                                                  CommandGraphNode *afterNode)
+{
+    afterNode->mParents.emplace_back(beforeNode);
+    beforeNode->setHasChildren();
+    ASSERT(beforeNode != afterNode && !beforeNode->isChildOf(afterNode));
+}
+
+// static
+void CommandGraphNode::SetHappensBeforeDependencies(
+    const std::vector<CommandGraphNode *> &beforeNodes,
+    CommandGraphNode *afterNode)
+{
+    afterNode->mParents.insert(afterNode->mParents.end(), beforeNodes.begin(), beforeNodes.end());
+
+    // TODO(jmadill): is there a faster way to do this?
+    for (CommandGraphNode *beforeNode : beforeNodes)
+    {
+        beforeNode->setHasChildren();
+
+        ASSERT(beforeNode != afterNode && !beforeNode->isChildOf(afterNode));
+    }
+}
+
+bool CommandGraphNode::hasParents() const
+{
+    return !mParents.empty();
+}
+
+void CommandGraphNode::setHasChildren()
+{
+    mHasChildren = true;
+}
+
+bool CommandGraphNode::hasChildren() const
+{
+    return mHasChildren;
+}
+
+// Do not call this in anything but testing code, since it's slow.
+bool CommandGraphNode::isChildOf(CommandGraphNode *parent)
+{
+    std::set<CommandGraphNode *> visitedList;
+    std::vector<CommandGraphNode *> openList;
+    openList.insert(openList.begin(), mParents.begin(), mParents.end());
+    while (!openList.empty())
+    {
+        CommandGraphNode *current = openList.back();
+        openList.pop_back();
+        if (visitedList.count(current) == 0)
+        {
+            if (current == parent)
+            {
+                return true;
+            }
+            visitedList.insert(current);
+            openList.insert(openList.end(), current->mParents.begin(), current->mParents.end());
+        }
+    }
+
+    return false;
+}
+
+VisitedState CommandGraphNode::visitedState() const
+{
+    return mVisitedState;
+}
+
+void CommandGraphNode::visitParents(std::vector<CommandGraphNode *> *stack)
+{
+    ASSERT(mVisitedState == VisitedState::Unvisited);
+    stack->insert(stack->end(), mParents.begin(), mParents.end());
+    mVisitedState = VisitedState::Ready;
+}
+
+Error CommandGraphNode::visitAndExecute(VkDevice device,
+                                        Serial serial,
+                                        RenderPassCache *renderPassCache,
+                                        CommandBuffer *primaryCommandBuffer)
+{
+    if (mOutsideRenderPassCommands.valid())
+    {
+        mOutsideRenderPassCommands.end();
+        primaryCommandBuffer->executeCommands(1, &mOutsideRenderPassCommands);
+    }
+
+    if (mInsideRenderPassCommands.valid())
+    {
+        // Pull a compatible RenderPass from the cache.
+        // TODO(jmadill): Insert real ops and layout transitions.
+        RenderPass *renderPass = nullptr;
+        ANGLE_TRY(
+            renderPassCache->getCompatibleRenderPass(device, serial, mRenderPassDesc, &renderPass));
+
+        mInsideRenderPassCommands.end();
+
+        VkRenderPassBeginInfo beginInfo;
+        beginInfo.sType                    = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+        beginInfo.pNext                    = nullptr;
+        beginInfo.renderPass               = renderPass->getHandle();
+        beginInfo.framebuffer              = mRenderPassFramebuffer.getHandle();
+        beginInfo.renderArea.offset.x      = static_cast<uint32_t>(mRenderPassRenderArea.x);
+        beginInfo.renderArea.offset.y      = static_cast<uint32_t>(mRenderPassRenderArea.y);
+        beginInfo.renderArea.extent.width  = static_cast<uint32_t>(mRenderPassRenderArea.width);
+        beginInfo.renderArea.extent.height = static_cast<uint32_t>(mRenderPassRenderArea.height);
+        beginInfo.clearValueCount          = mRenderPassDesc.attachmentCount();
+        beginInfo.pClearValues             = mRenderPassClearValues.data();
+
+        primaryCommandBuffer->beginRenderPass(beginInfo,
+                                              VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
+        primaryCommandBuffer->executeCommands(1, &mInsideRenderPassCommands);
+        primaryCommandBuffer->endRenderPass();
+    }
+
+    mVisitedState = VisitedState::Visited;
+    return NoError();
+}
+
+// CommandGraph implementation.
+CommandGraph::CommandGraph()
+{
+}
+
+CommandGraph::~CommandGraph()
+{
+    ASSERT(empty());
+}
+
+CommandGraphNode *CommandGraph::allocateNode()
+{
+    // TODO(jmadill): Use a pool allocator for the CPU node allocations.
+    CommandGraphNode *newCommands = new CommandGraphNode();
+    mNodes.emplace_back(newCommands);
+    return newCommands;
+}
+
+Error CommandGraph::submitCommands(VkDevice device,
+                                   Serial serial,
+                                   RenderPassCache *renderPassCache,
+                                   CommandPool *commandPool,
+                                   CommandBuffer *primaryCommandBufferOut)
+{
+    VkCommandBufferAllocateInfo primaryInfo;
+    primaryInfo.sType              = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+    primaryInfo.pNext              = nullptr;
+    primaryInfo.commandPool        = commandPool->getHandle();
+    primaryInfo.level              = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+    primaryInfo.commandBufferCount = 1;
+
+    ANGLE_TRY(primaryCommandBufferOut->init(device, primaryInfo));
+
+    if (mNodes.empty())
+    {
+        return NoError();
+    }
+
+    std::vector<CommandGraphNode *> nodeStack;
+
+    VkCommandBufferBeginInfo beginInfo;
+    beginInfo.sType            = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+    beginInfo.pNext            = nullptr;
+    beginInfo.flags            = 0;
+    beginInfo.pInheritanceInfo = nullptr;
+
+    ANGLE_TRY(primaryCommandBufferOut->begin(beginInfo));
+
+    for (CommandGraphNode *topLevelNode : mNodes)
+    {
+        // Only process commands that don't have child commands. The others will be pulled in
+        // automatically. Also skip commands that have already been visited.
+        if (topLevelNode->hasChildren() || topLevelNode->visitedState() != VisitedState::Unvisited)
+            continue;
+
+        nodeStack.push_back(topLevelNode);
+
+        while (!nodeStack.empty())
+        {
+            CommandGraphNode *node = nodeStack.back();
+
+            switch (node->visitedState())
+            {
+                case VisitedState::Unvisited:
+                    node->visitParents(&nodeStack);
+                    break;
+                case VisitedState::Ready:
+                    ANGLE_TRY(node->visitAndExecute(device, serial, renderPassCache,
+                                                    primaryCommandBufferOut));
+                    nodeStack.pop_back();
+                    break;
+                case VisitedState::Visited:
+                    nodeStack.pop_back();
+                    break;
+                default:
+                    UNREACHABLE();
+                    break;
+            }
+        }
+    }
+
+    ANGLE_TRY(primaryCommandBufferOut->end());
+
+    // TODO(jmadill): Use pool allocation so we don't need to deallocate command graph.
+    for (vk::CommandGraphNode *node : mNodes)
+    {
+        delete node;
+    }
+    mNodes.clear();
+
+    return NoError();
+}
+
+bool CommandGraph::empty() const
+{
+    return mNodes.empty();
+}
+
+}  // namespace vk
+}  // namespace rx