Add GrCap check for whether we can do a copy or not, and implement in Vk backend

Today, we only know if we fail a copy during the flush so we have no
way to cleanly handle a failed copy. This will allow us to know if we'll
fail a copy during recording and allow us to do some appropriate fallback
and/or dropping of the draw.

Bug: skia:
Change-Id: I38f209dbd4ebb4e762210b4609147d4b0a1b01b1
Reviewed-on: https://skia-review.googlesource.com/123560
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index ae1570c..1c36a14 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -74,6 +74,128 @@
     return true;
 }
 
+bool GrVkCaps::canCopyImage(GrPixelConfig dstConfig, int dstSampleCnt, GrSurfaceOrigin dstOrigin,
+                            GrPixelConfig srcConfig, int srcSampleCnt,
+                            GrSurfaceOrigin srcOrigin) const {
+    if ((dstSampleCnt > 1 || srcSampleCnt > 1) && dstSampleCnt != srcSampleCnt) {
+        return false;
+    }
+
+    // We require that all vulkan GrSurfaces have been created with transfer_dst and transfer_src
+    // as image usage flags.
+    if (srcOrigin != dstOrigin || GrBytesPerPixel(srcConfig) != GrBytesPerPixel(dstConfig)) {
+        return false;
+    }
+
+    if (this->shaderCaps()->configOutputSwizzle(srcConfig) !=
+        this->shaderCaps()->configOutputSwizzle(dstConfig)) {
+        return false;
+    }
+
+    return true;
+}
+
+bool GrVkCaps::canCopyAsBlit(GrPixelConfig dstConfig, int dstSampleCnt, bool dstIsLinear,
+                             GrPixelConfig srcConfig, int srcSampleCnt, bool srcIsLinear) const {
+    // We require that all vulkan GrSurfaces have been created with transfer_dst and transfer_src
+    // as image usage flags.
+    if (!this->configCanBeDstofBlit(dstConfig, dstIsLinear) ||
+        !this->configCanBeSrcofBlit(srcConfig, srcIsLinear)) {
+        return false;
+    }
+
+    if (this->shaderCaps()->configOutputSwizzle(srcConfig) !=
+        this->shaderCaps()->configOutputSwizzle(dstConfig)) {
+        return false;
+    }
+
+    // We cannot blit images that are multisampled. Will need to figure out if we can blit the
+    // resolved msaa though.
+    if (dstSampleCnt > 1 || srcSampleCnt > 1) {
+        return false;
+    }
+
+    return true;
+}
+
+bool GrVkCaps::canCopyAsResolve(GrPixelConfig dstConfig, int dstSampleCnt,
+                                GrSurfaceOrigin dstOrigin, GrPixelConfig srcConfig,
+                                int srcSampleCnt, GrSurfaceOrigin srcOrigin) const {
+    // The src surface must be multisampled.
+    if (srcSampleCnt <= 1) {
+        return false;
+    }
+
+    // The dst must not be multisampled.
+    if (dstSampleCnt > 1) {
+        return false;
+    }
+
+    // Surfaces must have the same format.
+    if (dstConfig != srcConfig) {
+        return false;
+    }
+
+    // Surfaces must have the same origin.
+    if (srcOrigin != dstOrigin) {
+        return false;
+    }
+
+    return true;
+}
+
+bool GrVkCaps::canCopyAsDraw(GrPixelConfig dstConfig, bool dstIsRenderable,
+                             GrPixelConfig srcConfig, bool srcIsTextureable) const {
+    // TODO: Make copySurfaceAsDraw handle the swizzle
+    if (this->shaderCaps()->configOutputSwizzle(srcConfig) !=
+        this->shaderCaps()->configOutputSwizzle(dstConfig)) {
+        return false;
+    }
+
+    // Make sure the dst is a render target and the src is a texture.
+    if (!dstIsRenderable || !srcIsTextureable) {
+        return false;
+    }
+
+    return true;
+}
+
+bool GrVkCaps::canCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
+                              const SkIRect& srcRect, const SkIPoint& dstPoint) const {
+    GrSurfaceOrigin dstOrigin = dst->origin();
+    GrSurfaceOrigin srcOrigin = src->origin();
+
+    GrPixelConfig dstConfig = dst->config();
+    GrPixelConfig srcConfig = src->config();
+
+    // TODO: Figure out a way to track if we've wrapped a linear texture in a proxy (e.g.
+    // PromiseImage which won't get instantiated right away. Does this need a similar thing like the
+    // tracking of external or rectangle textures in GL? For now we don't create linear textures
+    // internally, and I don't believe anyone is wrapping them.
+    bool srcIsLinear = false;
+    bool dstIsLinear = false;
+
+    int dstSampleCnt = 0;
+    int srcSampleCnt = 0;
+    if (const GrRenderTargetProxy* rtProxy = dst->asRenderTargetProxy()) {
+        dstSampleCnt = rtProxy->numColorSamples();
+    }
+    if (const GrRenderTargetProxy* rtProxy = src->asRenderTargetProxy()) {
+        srcSampleCnt = rtProxy->numColorSamples();
+    }
+    SkASSERT((dstSampleCnt > 0) == SkToBool(dst->asRenderTargetProxy()));
+    SkASSERT((srcSampleCnt > 0) == SkToBool(src->asRenderTargetProxy()));
+
+    return this->canCopyImage(dstConfig, dstSampleCnt, dstOrigin,
+                              srcConfig, srcSampleCnt, srcOrigin) ||
+           this->canCopyAsBlit(dstConfig, dstSampleCnt, dstIsLinear,
+                               srcConfig, srcSampleCnt, srcIsLinear) ||
+           this->canCopyAsResolve(dstConfig, dstSampleCnt, dstOrigin,
+                                  srcConfig, srcSampleCnt, srcOrigin) ||
+           this->canCopyAsDraw(dstConfig, dstSampleCnt > 0,
+                               srcConfig, SkToBool(src->asTextureProxy()));
+}
+
 void GrVkCaps::init(const GrContextOptions& contextOptions, const GrVkInterface* vkInterface,
                     VkPhysicalDevice physDev, uint32_t featureFlags, uint32_t extensionFlags) {