Vulkan: Framebuffer blit support for depth/stencil cases

Bug: angleproject:2643
Change-Id: Ib50e4051f5b3965c2a752cf2cd45d3470312cdcf
Reviewed-on: https://chromium-review.googlesource.com/1115370
Commit-Queue: Luc Ferron <lucferron@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
index ba969ca..7f24ff5 100644
--- a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
+++ b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
@@ -342,6 +342,74 @@
     return mRenderTargetCache.getDepthStencil();
 }
 
+gl::Error FramebufferVk::blitUsingCopy(RendererVk *renderer,
+                                       vk::CommandBuffer *commandBuffer,
+                                       const gl::Rectangle &readArea,
+                                       const gl::Rectangle &destArea,
+                                       RenderTargetVk *readRenderTarget,
+                                       RenderTargetVk *drawRenderTarget,
+                                       const gl::Rectangle *scissor,
+                                       bool blitDepthBuffer,
+                                       bool blitStencilBuffer)
+{
+    gl::Rectangle scissoredDrawRect = destArea;
+    gl::Rectangle scissoredReadRect = readArea;
+
+    if (scissor)
+    {
+        if (!ClipRectangle(destArea, *scissor, &scissoredDrawRect))
+        {
+            return gl::NoError();
+        }
+
+        if (!ClipRectangle(readArea, *scissor, &scissoredReadRect))
+        {
+            return gl::NoError();
+        }
+    }
+
+    const gl::Extents sourceFrameBufferExtents = readRenderTarget->getImageExtents();
+    const gl::Extents drawFrameBufferExtents   = drawRenderTarget->getImageExtents();
+
+    // After cropping for the scissor, we also want to crop for the size of the buffers.
+    gl::Rectangle readFrameBufferBounds(0, 0, sourceFrameBufferExtents.width,
+                                        sourceFrameBufferExtents.height);
+    gl::Rectangle drawFrameBufferBounds(0, 0, drawFrameBufferExtents.width,
+                                        drawFrameBufferExtents.height);
+    if (!ClipRectangle(scissoredReadRect, readFrameBufferBounds, &scissoredReadRect))
+    {
+        return gl::NoError();
+    }
+
+    if (!ClipRectangle(scissoredDrawRect, drawFrameBufferBounds, &scissoredDrawRect))
+    {
+        return gl::NoError();
+    }
+
+    ASSERT(readFrameBufferBounds == drawFrameBufferBounds);
+    ASSERT(scissoredReadRect == readFrameBufferBounds);
+    ASSERT(scissoredDrawRect == drawFrameBufferBounds);
+
+    VkFlags aspectFlags =
+        vk::GetDepthStencilAspectFlags(readRenderTarget->getImageFormat().textureFormat());
+    vk::ImageHelper *readImage = readRenderTarget->getImageForRead(
+        this, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, aspectFlags, commandBuffer);
+    vk::ImageHelper *writeImage = drawRenderTarget->getImageForWrite(this);
+    // Requirement of the copyImageToBuffer, the dst image must be in
+    // VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL layout.
+    writeImage->changeLayoutWithStages(aspectFlags, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+                                       VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+                                       VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, commandBuffer);
+    VkImageAspectFlags depthBit   = blitDepthBuffer ? VK_IMAGE_ASPECT_DEPTH_BIT : 0;
+    VkImageAspectFlags stencilBit = blitStencilBuffer ? VK_IMAGE_ASPECT_STENCIL_BIT : 0;
+    VkImageAspectFlags aspectMask = depthBit | stencilBit;
+    vk::ImageHelper::Copy(readImage, writeImage, gl::Offset(), gl::Offset(),
+                          gl::Extents(scissoredDrawRect.width, scissoredDrawRect.height, 1),
+                          aspectMask, commandBuffer);
+
+    return gl::NoError();
+}
+
 RenderTargetVk *FramebufferVk::getColorReadRenderTarget() const
 {
     RenderTargetVk *renderTarget = mRenderTargetCache.getColorRead(mState);
@@ -368,7 +436,6 @@
     vk::CommandBuffer *commandBuffer = nullptr;
     ANGLE_TRY(beginWriteResource(renderer, &commandBuffer));
     FramebufferVk *sourceFramebufferVk = vk::GetImpl(sourceFramebuffer);
-
     if (blitColorBuffer)
     {
         RenderTargetVk *readRenderTarget = sourceFramebufferVk->getColorReadRenderTarget();
@@ -400,10 +467,9 @@
         }
         else
         {
-            // TODO(lucferron): Support framebuffer blit with a slower path.
-            // http://anglebug.com/2643
-            UNIMPLEMENTED();
-            return gl::InternalError();
+            ASSERT(filter == GL_NEAREST);
+            ANGLE_TRY(blitUsingCopy(renderer, commandBuffer, sourceArea, destArea, readRenderTarget,
+                                    drawRenderTarget, scissor, blitDepthBuffer, blitStencilBuffer));
         }
     }