Vulkan: Call GraphResource instead of GraphNode.

We don't need to use the CommandGraphNode class directly. This CL
consolidates our code so we never call the GraphNodes class directly.
Instead we call operations on GraphResource. This should simplify the
interaction with APIs from the various graph and dependency management
classes in the Vulkan back-end.

A new concept of 'starting' vs 'appending' commands is introduced.
Appending tries to avoid starting new command buffers when possible.

Should not change how the graphs are constructed, and mostly be a
refactoring change. There may be minor behaviour changes to some
commands.

Bug: angleproject:2539
Change-Id: Ia971e5cacb1164b9b3b22fa4a0a55b954d81f10e
Reviewed-on: https://chromium-review.googlesource.com/1052068
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Frank Henigman <fjhenigman@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
index 37037d4..c945e44 100644
--- a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
+++ b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
@@ -114,7 +114,6 @@
 
     // This command buffer is only started once.
     vk::CommandBuffer *commandBuffer  = nullptr;
-    vk::CommandGraphNode *writingNode = nullptr;
 
     const gl::FramebufferAttachment *depthAttachment = mState.getDepthAttachment();
     bool clearDepth = (depthAttachment && (mask & GL_DEPTH_BUFFER_BIT) != 0);
@@ -173,7 +172,6 @@
     if (clearDepth || clearStencil)
     {
         ANGLE_TRY(beginWriteResource(renderer, &commandBuffer));
-        writingNode = getCurrentWritingNode();
 
         const VkClearDepthStencilValue &clearDepthStencilValue =
             contextVk->getClearDepthStencilValue().depthStencil;
@@ -186,7 +184,7 @@
             (stencilAttachment ? VK_IMAGE_ASPECT_STENCIL_BIT : 0);
 
         RenderTargetVk *renderTarget = mRenderTargetCache.getDepthStencil();
-        vk::ImageHelper *image       = renderTarget->getImageForWrite(currentSerial, writingNode);
+        vk::ImageHelper *image       = renderTarget->getImageForWrite(currentSerial, this);
         image->clearDepthStencil(aspectFlags, clearDepthStencilValue, commandBuffer);
 
         if (!clearColor)
@@ -202,7 +200,6 @@
     if (!commandBuffer)
     {
         ANGLE_TRY(beginWriteResource(renderer, &commandBuffer));
-        writingNode = getCurrentWritingNode();
     }
 
     // TODO(jmadill): Support gaps in RenderTargets. http://anglebug.com/2394
@@ -212,7 +209,7 @@
     {
         RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndex];
         ASSERT(colorRenderTarget);
-        vk::ImageHelper *image = colorRenderTarget->getImageForWrite(currentSerial, writingNode);
+        vk::ImageHelper *image = colorRenderTarget->getImageForWrite(currentSerial, this);
         image->clearColor(clearColorValue, commandBuffer);
     }
 
@@ -392,8 +389,9 @@
     mRenderPassDesc.reset();
     renderer->releaseResource(*this, &mFramebuffer);
 
-    // Trigger a new set of secondary commands next time we render to this FBO.
-    getNewWritingNode(renderer);
+    // Will freeze the current set of dependencies on this FBO. The next time we render we will
+    // create a new vk::CommandGraphNode.
+    onResourceChanged(renderer);
 
     contextVk->invalidateCurrentPipeline();
 
@@ -498,23 +496,14 @@
                                                    bool clearDepth,
                                                    bool clearStencil)
 {
-    RendererVk *renderer = contextVk->getRenderer();
-
-    getNewWritingNode(renderer);
+    // Trigger a new command node to ensure overlapping writes happen sequentially.
+    onResourceChanged(contextVk->getRenderer());
 
     // This command can only happen inside a render pass, so obtain one if its already happening
     // or create a new one if not.
-    vk::CommandGraphNode *node       = nullptr;
     vk::CommandBuffer *commandBuffer = nullptr;
-    ANGLE_TRY(getCommandGraphNodeForDraw(contextVk, &node));
-    if (node->getInsideRenderPassCommands()->valid())
-    {
-        commandBuffer = node->getInsideRenderPassCommands();
-    }
-    else
-    {
-        ANGLE_TRY(node->beginInsideRenderPassRecording(renderer, &commandBuffer));
-    }
+    vk::RecordingMode mode           = vk::RecordingMode::Start;
+    ANGLE_TRY(getCommandBufferForDraw(contextVk, &commandBuffer, &mode));
 
     // TODO(jmadill): Cube map attachments. http://anglebug.com/2470
     // We assume for now that we always need to clear only 1 layer starting at the
@@ -527,7 +516,7 @@
     // When clearing, the scissor region must be clipped to the renderArea per the validation rules
     // in Vulkan.
     gl::Rectangle intersection;
-    if (!gl::ClipRectangle(contextVk->getGLState().getScissor(), node->getRenderPassRenderArea(),
+    if (!gl::ClipRectangle(contextVk->getGLState().getScissor(), getRenderPassRenderArea(),
                            &intersection))
     {
         // There is nothing to clear since the scissor is outside of the render area.
@@ -592,6 +581,9 @@
     RendererVk *renderer             = contextVk->getRenderer();
     vk::ShaderLibrary *shaderLibrary = renderer->getShaderLibrary();
 
+    // Trigger a new command node to ensure overlapping writes happen sequentially.
+    onResourceChanged(renderer);
+
     const vk::ShaderAndSerial *fullScreenQuad = nullptr;
     ANGLE_TRY(shaderLibrary->getShader(renderer, vk::InternalShaderID::FullScreenQuad_vert,
                                        &fullScreenQuad));
@@ -603,11 +595,11 @@
     const vk::PipelineLayout *pipelineLayout = nullptr;
     ANGLE_TRY(renderer->getInternalPushConstantPipelineLayout(&pipelineLayout));
 
-    vk::CommandBuffer *commandBuffer = nullptr;
-    ANGLE_TRY(beginWriteResource(renderer, &commandBuffer));
+    vk::RecordingMode recordingMode = vk::RecordingMode::Start;
+    vk::CommandBuffer *drawCommands = nullptr;
+    ANGLE_TRY(getCommandBufferForDraw(contextVk, &drawCommands, &recordingMode));
 
-    vk::CommandGraphNode *node = nullptr;
-    ANGLE_TRY(getCommandGraphNodeForDraw(contextVk, &node));
+    const gl::Rectangle &renderArea = getRenderPassRenderArea();
 
     // This pipeline desc could be cached.
     vk::PipelineDesc pipelineDesc;
@@ -615,14 +607,13 @@
     pipelineDesc.updateColorWriteMask(colorMaskFlags);
     pipelineDesc.updateRenderPassDesc(getRenderPassDesc());
     pipelineDesc.updateShaders(fullScreenQuad->queueSerial(), pushConstantColor->queueSerial());
-    pipelineDesc.updateViewport(node->getRenderPassRenderArea(), 0.0f, 1.0f);
+    pipelineDesc.updateViewport(renderArea, 0.0f, 1.0f);
 
     const gl::State &glState = contextVk->getGLState();
     if (glState.isScissorTestEnabled())
     {
         gl::Rectangle intersection;
-        if (!gl::ClipRectangle(glState.getScissor(), node->getRenderPassRenderArea(),
-                               &intersection))
+        if (!gl::ClipRectangle(glState.getScissor(), renderArea, &intersection))
         {
             return gl::NoError();
         }
@@ -631,7 +622,7 @@
     }
     else
     {
-        pipelineDesc.updateScissor(node->getRenderPassRenderArea());
+        pipelineDesc.updateScissor(renderArea);
     }
 
     vk::PipelineAndSerial *pipeline = nullptr;
@@ -639,18 +630,17 @@
                                             pipelineDesc, gl::AttributesMask(), &pipeline));
     pipeline->updateSerial(renderer->getCurrentQueueSerial());
 
-
-    vk::CommandBuffer *drawCommandBuffer = nullptr;
-    ANGLE_TRY(node->beginInsideRenderPassRecording(renderer, &drawCommandBuffer));
+    vk::CommandBuffer *writeCommands = nullptr;
+    ANGLE_TRY(appendWriteResource(renderer, &writeCommands));
 
     VkClearColorValue clearColorValue = contextVk->getClearColorValue().color;
-    drawCommandBuffer->pushConstants(*pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0,
-                                     sizeof(VkClearColorValue), clearColorValue.float32);
+    drawCommands->pushConstants(*pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0,
+                                sizeof(VkClearColorValue), clearColorValue.float32);
 
     // TODO(jmadill): Masked combined color and depth/stencil clear. http://anglebug.com/2455
     // Any active queries submitted by the user should also be paused here.
-    drawCommandBuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->get());
-    drawCommandBuffer->draw(6, 1, 0, 0);
+    drawCommands->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->get());
+    drawCommands->draw(6, 1, 0, 0);
 
     return gl::NoError();
 }
@@ -663,40 +653,31 @@
     return gl::InternalError() << "getSamplePosition is unimplemented.";
 }
 
-gl::Error FramebufferVk::getCommandGraphNodeForDraw(ContextVk *contextVk,
-                                                    vk::CommandGraphNode **nodeOut)
+gl::Error FramebufferVk::getCommandBufferForDraw(ContextVk *contextVk,
+                                                 vk::CommandBuffer **commandBufferOut,
+                                                 vk::RecordingMode *modeOut)
 {
     RendererVk *renderer = contextVk->getRenderer();
     Serial currentSerial = renderer->getCurrentQueueSerial();
 
-    // This will reset the current writing node if it has been completed.
+    // This will clear the current write operation if it is complete.
     updateQueueSerial(currentSerial);
 
-    if (hasChildlessWritingNode())
+    if (hasStartedRenderPass())
     {
-        *nodeOut = getCurrentWritingNode();
-    }
-    else
-    {
-        *nodeOut = getNewWritingNode(renderer);
-    }
-
-    if ((*nodeOut)->getInsideRenderPassCommands()->valid())
-    {
+        appendToRenderPass(commandBufferOut);
+        *modeOut = vk::RecordingMode::Append;
         return gl::NoError();
     }
 
     vk::Framebuffer *framebuffer = nullptr;
     ANGLE_TRY_RESULT(getFramebuffer(renderer), framebuffer);
 
+    // TODO(jmadill): Proper clear value implementation. http://anglebug.com/2361
     std::vector<VkClearValue> attachmentClearValues;
 
-    if (!(*nodeOut)->getOutsideRenderPassCommands()->valid())
-    {
-        vk::CommandBuffer *commandBuffer = nullptr;
-        ANGLE_TRY((*nodeOut)->beginOutsideRenderPassRecording(
-            renderer->getDevice(), renderer->getCommandPool(), &commandBuffer));
-    }
+    vk::CommandBuffer *writeCommands = nullptr;
+    ANGLE_TRY(appendWriteResource(renderer, &writeCommands));
 
     vk::RenderPassDesc renderPassDesc;
 
@@ -708,25 +689,23 @@
         RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndex];
         ASSERT(colorRenderTarget);
 
-        colorRenderTarget->onColorDraw(currentSerial, *nodeOut, &renderPassDesc);
+        colorRenderTarget->onColorDraw(this, writeCommands, &renderPassDesc);
         attachmentClearValues.emplace_back(contextVk->getClearColorValue());
     }
 
     RenderTargetVk *depthStencilRenderTarget = mRenderTargetCache.getDepthStencil();
     if (depthStencilRenderTarget)
     {
-        depthStencilRenderTarget->onDepthStencilDraw(currentSerial, *nodeOut, &renderPassDesc);
+        depthStencilRenderTarget->onDepthStencilDraw(this, writeCommands, &renderPassDesc);
         attachmentClearValues.emplace_back(contextVk->getClearDepthStencilValue());
     }
 
     gl::Rectangle renderArea =
         gl::Rectangle(0, 0, mState.getDimensions().width, mState.getDimensions().height);
-    // Hard-code RenderPass to clear the first render target to the current clear value.
-    // TODO(jmadill): Proper clear value implementation. http://anglebug.com/2361
-    (*nodeOut)->storeRenderPassInfo(*framebuffer, renderArea, renderPassDesc,
-                                    attachmentClearValues);
 
-    return gl::NoError();
+    *modeOut = vk::RecordingMode::Start;
+    return beginRenderPass(renderer, *framebuffer, renderArea, mRenderPassDesc.value(),
+                           attachmentClearValues, commandBufferOut);
 }
 
 void FramebufferVk::updateActiveColorMasks(size_t colorIndex, bool r, bool g, bool b, bool a)
@@ -759,7 +738,7 @@
     // Note that although we're reading from the image, we need to update the layout below.
     // TODO(jmadill): Clearify read/write semantics. http://anglebug.com/2539
     vk::ImageHelper *srcImage =
-        renderTarget->getImageForWrite(renderer->getCurrentQueueSerial(), getCurrentWritingNode());
+        renderTarget->getImageForWrite(renderer->getCurrentQueueSerial(), this);
 
     const angle::Format &angleFormat = srcImage->getFormat().textureFormat();
     VkBuffer bufferHandle            = VK_NULL_HANDLE;