Rework GL fbo blit limitations as finer grained flags.

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=3480
BUG=skia:5804

Change-Id: Idd86f96a92420ec98c7cf07962f4857e0b81b9c6
Reviewed-on: https://skia-review.googlesource.com/3480
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index c2e8f11..23b069f 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -52,7 +52,7 @@
     fRGBAToBGRAReadbackConversionsAreSlow = false;
     fDoManualMipmapping = false;
 
-    fBlitFramebufferSupport = kNone_BlitFramebufferSupport;
+    fBlitFramebufferFlags = kNoSupport_BlitFramebufferFlag;
 
     fShaderCaps.reset(new GrGLSLCaps(contextOptions));
 
@@ -919,8 +919,6 @@
 }
 
 void GrGLCaps::initFSAASupport(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) {
-
-    fMSFBOType = kNone_MSFBOType;
     if (kGL_GrGLStandard != ctxInfo.standard()) {
         // We prefer the EXT/IMG extension over ES3 MSAA because we've observed
         // ES3 driver bugs on at least one device with a tiled GPU (N10).
@@ -940,24 +938,28 @@
         // Above determined the preferred MSAA approach, now decide whether glBlitFramebuffer
         // is available.
         if (ctxInfo.version() >= GR_GL_VER(3, 0)) {
-            fBlitFramebufferSupport = kFull_BlitFramebufferSupport;
+            fBlitFramebufferFlags = kNoFormatConversionForMSAASrc_BlitFramebufferFlag |
+                                    kRectsMustMatchForMSAASrc_BlitFramebufferFlag;
         } else if (ctxInfo.hasExtension("GL_CHROMIUM_framebuffer_multisample")) {
             // The CHROMIUM extension uses the ANGLE version of glBlitFramebuffer and includes its
             // limitations.
-            fBlitFramebufferSupport = kNoScalingNoMirroring_BlitFramebufferSupport;
+            fBlitFramebufferFlags = kNoScalingOrMirroring_BlitFramebufferFlag |
+                                    kResolveMustBeFull_BlitFrambufferFlag |
+                                    kNoMSAADst_BlitFramebufferFlag |
+                                    kNoFormatConversion_BlitFramebufferFlag;
         }
     } else {
         if (fUsesMixedSamples) {
             fMSFBOType = kMixedSamples_MSFBOType;
-            fBlitFramebufferSupport = kFull_BlitFramebufferSupport;
+            fBlitFramebufferFlags = 0;
         } else if (ctxInfo.version() >= GR_GL_VER(3,0) ||
                    ctxInfo.hasExtension("GL_ARB_framebuffer_object")) {
             fMSFBOType = kStandard_MSFBOType;
-            fBlitFramebufferSupport = kFull_BlitFramebufferSupport;
+            fBlitFramebufferFlags = 0;
         } else if (ctxInfo.hasExtension("GL_EXT_framebuffer_multisample") &&
                    ctxInfo.hasExtension("GL_EXT_framebuffer_blit")) {
             fMSFBOType = kEXT_MSFBOType;
-            fBlitFramebufferSupport = kFull_BlitFramebufferSupport;
+            fBlitFramebufferFlags = 0;
         }
     }
 }
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index e162b38..794bb75 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -73,19 +73,14 @@
         kLast_MSFBOType = kMixedSamples_MSFBOType
     };
 
-    enum BlitFramebufferSupport {
-        kNone_BlitFramebufferSupport,
-        /**
-         * ANGLE exposes a limited blit framebuffer extension that does not allow for stretching
-         * or mirroring.
-         */
-        kNoScalingNoMirroring_BlitFramebufferSupport,
-        /**
-         * ES3 has restricted support when the src is MSAA: src rect == dst rect, dst format == src
-         * format.
-         */
-        kRectsAndFormatsMatchForMSAASrc_BlitFramebufferSupport,
-        kFull_BlitFramebufferSupport
+    enum BlitFramebufferFlags {
+        kNoSupport_BlitFramebufferFlag                    = 1 << 0,
+        kNoScalingOrMirroring_BlitFramebufferFlag         = 1 << 1,
+        kResolveMustBeFull_BlitFrambufferFlag             = 1 << 2,
+        kNoMSAADst_BlitFramebufferFlag                    = 1 << 3,
+        kNoFormatConversion_BlitFramebufferFlag           = 1 << 4,
+        kNoFormatConversionForMSAASrc_BlitFramebufferFlag = 1 << 5,
+        kRectsMustMatchForMSAASrc_BlitFramebufferFlag     = 1 << 6,
     };
 
     enum InvalidateFBType {
@@ -235,7 +230,7 @@
     /**
      * What functionality is supported by glBlitFramebuffer.
      */
-    BlitFramebufferSupport blitFramebufferSupport() const { return fBlitFramebufferSupport; }
+    uint32_t blitFramebufferSupportFlags() const { return fBlitFramebufferFlags; }
 
     /**
      * Is the MSAA FBO extension one where the texture is multisampled when bound to an FBO and
@@ -419,7 +414,7 @@
     bool fRGBAToBGRAReadbackConversionsAreSlow : 1;
     bool fDoManualMipmapping : 1;
 
-    BlitFramebufferSupport fBlitFramebufferSupport;
+    uint32_t fBlitFramebufferFlags;
 
     /** Number type of the components (with out considering number of bits.) */
     enum FormatType {
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index eae0aa9..6b534a0 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -2921,18 +2921,27 @@
                 this->disableWindowRectangles();
                 GL_CALL(ResolveMultisampleFramebuffer());
             } else {
-                GrGLIRect r;
-                r.setRelativeTo(vp, dirtyRect.fLeft, dirtyRect.fTop,
-                                dirtyRect.width(), dirtyRect.height(), target->origin());
-
-                int right = r.fLeft + r.fWidth;
-                int top = r.fBottom + r.fHeight;
+                int l, b, r, t;
+                if (GrGLCaps::kResolveMustBeFull_BlitFrambufferFlag &
+                    this->glCaps().blitFramebufferSupportFlags()) {
+                    l = 0;
+                    b = 0;
+                    r = target->width();
+                    t = target->height();
+                } else {
+                    GrGLIRect rect;
+                    rect.setRelativeTo(vp, dirtyRect.fLeft, dirtyRect.fTop,
+                                       dirtyRect.width(), dirtyRect.height(), target->origin());
+                    l = rect.fLeft;
+                    b = rect.fBottom;
+                    r = rect.fLeft + rect.fWidth;
+                    t = rect.fBottom + rect.fHeight;
+                }
 
                 // BlitFrameBuffer respects the scissor, so disable it.
                 this->disableScissor();
                 this->disableWindowRectangles();
-                GL_CALL(BlitFramebuffer(r.fLeft, r.fBottom, right, top,
-                                        r.fLeft, r.fBottom, right, top,
+                GL_CALL(BlitFramebuffer(l, b, r, t, l, b, r, t,
                                         GR_GL_COLOR_BUFFER_BIT, GR_GL_NEAREST));
             }
         }
@@ -3472,45 +3481,64 @@
     fHWBoundTextureUniqueIDs[lastUnitIdx] = SK_InvalidUniqueID;
 }
 
-// Determines whether glBlitFramebuffer could be used between src and dst.
-static inline bool can_blit_framebuffer(const GrSurface* dst,
-                                        const GrSurface* src,
-                                        const GrGLGpu* gpu) {
-    if (gpu->glCaps().isConfigRenderable(dst->config(), dst->desc().fSampleCnt > 0) &&
-        gpu->glCaps().isConfigRenderable(src->config(), src->desc().fSampleCnt > 0)) {
-        switch (gpu->glCaps().blitFramebufferSupport()) {
-            case GrGLCaps::kNone_BlitFramebufferSupport:
-                return false;
-            case GrGLCaps::kNoScalingNoMirroring_BlitFramebufferSupport:
-                // Our copy surface doesn't support scaling so just check for mirroring.
-                if (dst->origin() != src->origin()) {
-                    return false;
-                }
-                break;
-                // ES3 doesn't allow framebuffer blits when the src has MSAA and the configs don't
-                // match or the rects are not the same (not just the same size but have the same
-                // edges).
-            case GrGLCaps::kRectsAndFormatsMatchForMSAASrc_BlitFramebufferSupport:
-                if ((src->desc().fSampleCnt > 0 || src->config() != dst->config())) {
-                    return false;
-                }
-                break;
-
-            case GrGLCaps::kFull_BlitFramebufferSupport:
-                return true;
-        }
-        const GrGLTexture* dstTex = static_cast<const GrGLTexture*>(dst->asTexture());
-        if (dstTex && dstTex->target() != GR_GL_TEXTURE_2D) {
-            return false;
-        }
-        const GrGLTexture* srcTex = static_cast<const GrGLTexture*>(dst->asTexture());
-        if (srcTex && srcTex->target() != GR_GL_TEXTURE_2D) {
-            return false;
-        }
-        return true;
-    } else {
+// Determines whether glBlitFramebuffer could be used between src and dst by onCopySurface.
+static inline bool can_blit_framebuffer_for_copy_surface(const GrSurface* dst,
+                                                         const GrSurface* src,
+                                                         const SkIRect& srcRect,
+                                                         const SkIPoint& dstPoint,
+                                                         const GrGLGpu* gpu) {
+    auto blitFramebufferFlags = gpu->glCaps().blitFramebufferSupportFlags();
+    if (!gpu->glCaps().isConfigRenderable(dst->config(), dst->desc().fSampleCnt > 0) ||
+        !gpu->glCaps().isConfigRenderable(src->config(), src->desc().fSampleCnt > 0)) {
         return false;
     }
+    const GrGLTexture* dstTex = static_cast<const GrGLTexture*>(dst->asTexture());
+    const GrGLTexture* srcTex = static_cast<const GrGLTexture*>(dst->asTexture());
+    const GrRenderTarget* dstRT = dst->asRenderTarget();
+    const GrRenderTarget* srcRT = src->asRenderTarget();
+    if (dstTex && dstTex->target() != GR_GL_TEXTURE_2D) {
+        return false;
+    }
+    if (srcTex && srcTex->target() != GR_GL_TEXTURE_2D) {
+        return false;
+    }
+    if (GrGLCaps::kNoSupport_BlitFramebufferFlag & blitFramebufferFlags) {
+        return false;
+    }
+    if (GrGLCaps::kNoScalingOrMirroring_BlitFramebufferFlag & blitFramebufferFlags) {
+        // We would mirror to compensate for origin changes. Note that copySurface is
+        // specified such that the src and dst rects are the same.
+        if (dst->origin() != src->origin()) {
+            return false;
+        }
+    }
+    if (GrGLCaps::kResolveMustBeFull_BlitFrambufferFlag & blitFramebufferFlags) {
+        if (srcRT && srcRT->numColorSamples() && dstRT && !dstRT->numColorSamples()) {
+            return false;
+        }
+    }
+    if (GrGLCaps::kNoMSAADst_BlitFramebufferFlag & blitFramebufferFlags) {
+        if (dstRT && dstRT->numColorSamples() > 0) {
+            return false;
+        }
+    }    
+    if (GrGLCaps::kNoFormatConversion_BlitFramebufferFlag & blitFramebufferFlags) {
+        if (dst->config() != src->config()) {
+            return false;
+        }
+    } else if (GrGLCaps::kNoFormatConversionForMSAASrc_BlitFramebufferFlag & blitFramebufferFlags) {
+        const GrRenderTarget* srcRT = src->asRenderTarget();
+        if (srcRT && srcRT->numColorSamples() && dst->config() != src->config()) {
+            return false;
+        }
+    }
+    if (GrGLCaps::kRectsMustMatchForMSAASrc_BlitFramebufferFlag & blitFramebufferFlags) {
+        if (srcRT && srcRT->numColorSamples() &&
+            (dstPoint.fX != srcRect.fLeft || dstPoint.fY != srcRect.fTop)) {
+            return false;
+        }
+    }
+    return true;
 }
 
 static inline bool can_copy_texsubimage(const GrSurface* dst,
@@ -3627,8 +3655,8 @@
     // creation. It isn't clear that avoiding temporary fbo creation is actually optimal.
 
     GrSurfaceOrigin originForBlitFramebuffer = kDefault_GrSurfaceOrigin;
-    if (this->glCaps().blitFramebufferSupport() ==
-        GrGLCaps::kNoScalingNoMirroring_BlitFramebufferSupport) {
+    if (this->glCaps().blitFramebufferSupportFlags() &
+        GrGLCaps::kNoScalingOrMirroring_BlitFramebufferFlag) {
         originForBlitFramebuffer = src->origin();
     }
 
@@ -3689,7 +3717,7 @@
         return true;
     }
 
-    if (can_blit_framebuffer(dst, src, this)) {
+    if (can_blit_framebuffer_for_copy_surface(dst, src, srcRect, dstPoint, this)) {
         return this->copySurfaceAsBlitFramebuffer(dst, src, srcRect, dstPoint);
     }
 
@@ -4279,7 +4307,7 @@
                                            GrSurface* src,
                                            const SkIRect& srcRect,
                                            const SkIPoint& dstPoint) {
-    SkASSERT(can_blit_framebuffer(dst, src, this));
+    SkASSERT(can_blit_framebuffer_for_copy_surface(dst, src, srcRect, dstPoint, this));
     SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY,
                                         srcRect.width(), srcRect.height());
     if (dst == src) {