Add flag for window rectangles to GrRenderTarget

Adds a flag to GrRenderTarget that indicates whether it can be used
with window rectangles. Also attempts to clean up some of the mixed
samples API.

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

Review-Url: https://codereview.chromium.org/2225303002
diff --git a/include/gpu/GrDrawContext.h b/include/gpu/GrDrawContext.h
index 6b582a6..def541d 100644
--- a/include/gpu/GrDrawContext.h
+++ b/include/gpu/GrDrawContext.h
@@ -263,7 +263,7 @@
         return fRenderTarget->isStencilBufferMultisampled();
     }
     bool isUnifiedMultisampled() const { return fRenderTarget->isUnifiedMultisampled(); }
-    bool hasMixedSamples() const { return fRenderTarget->hasMixedSamples(); }
+    bool hasMixedSamples() const { return fRenderTarget->isMixedSampled(); }
 
     bool mustUseHWAA(const GrPaint& paint) const {
         return paint.isAntiAlias() && fRenderTarget->isUnifiedMultisampled();
diff --git a/include/gpu/GrRenderTarget.h b/include/gpu/GrRenderTarget.h
index 77b04d2..1f87787 100644
--- a/include/gpu/GrRenderTarget.h
+++ b/include/gpu/GrRenderTarget.h
@@ -30,66 +30,28 @@
     const GrRenderTarget* asRenderTarget() const  override { return this; }
 
     // GrRenderTarget
-    /**
-     * On some hardware it is possible for a render target to have multisampling
-     * only in certain buffers.
-     * Enforce only two legal sample configs.
-     * kUnified_SampleConfig signifies multisampling in both color and stencil
-     * buffers and is available across all hardware.
-     * kStencil_SampleConfig means multisampling is present in stencil buffer
-     * only; this config requires hardware support of
-     * NV_framebuffer_mixed_samples.
-    */
-    enum SampleConfig {
-        kUnified_SampleConfig = 0,
-        kStencil_SampleConfig = 1
-    };
+    bool isStencilBufferMultisampled() const { return fDesc.fSampleCnt > 0; }
 
     /**
-     * @return true if the surface is multisampled in all buffers,
-     *         false otherwise
+     * For our purposes, "Mixed Sampled" means the stencil buffer is multisampled but the color
+     * buffer is not.
      */
-    bool isUnifiedMultisampled() const {
-        if (fSampleConfig != kUnified_SampleConfig) {
-            return false;
-        }
-        return 0 != fDesc.fSampleCnt;
-    }
+    bool isMixedSampled() const { return fFlags & Flags::kMixedSampled; }
 
     /**
-     * @return true if the surface is multisampled in the stencil buffer,
-     *         false otherwise
+     * "Unified Sampled" means the stencil and color buffers are both multisampled.
      */
-    bool isStencilBufferMultisampled() const {
-        return 0 != fDesc.fSampleCnt;
-    }
+    bool isUnifiedMultisampled() const { return fDesc.fSampleCnt > 0 && !this->isMixedSampled(); }
 
     /**
-     * @return the number of color samples-per-pixel, or zero if non-MSAA or
-     *         multisampled in the stencil buffer only.
+     * Returns the number of samples/pixel in the stencil buffer (Zero if non-MSAA).
      */
-    int numColorSamples() const {
-        if (fSampleConfig == kUnified_SampleConfig) {
-            return fDesc.fSampleCnt;
-        }
-        return 0;
-    }
+    int numStencilSamples() const { return fDesc.fSampleCnt; }
 
     /**
-     * @return the number of stencil samples-per-pixel, or zero if non-MSAA.
+     * Returns the number of samples/pixel in the color buffer (Zero if non-MSAA or mixed sampled).
      */
-    int numStencilSamples() const {
-        return fDesc.fSampleCnt;
-    }
-
-    /**
-     * @return true if the surface is mixed sampled, false otherwise.
-     */
-    bool hasMixedSamples() const {
-        SkASSERT(kStencil_SampleConfig != fSampleConfig ||
-                 this->isStencilBufferMultisampled());
-        return kStencil_SampleConfig == fSampleConfig;
-    }
+    int numColorSamples() const { return this->isMixedSampled() ? 0 : fDesc.fSampleCnt; }
 
     /**
      * Call to indicate the multisample contents were modified such that the
@@ -156,19 +118,17 @@
     void setLastDrawTarget(GrDrawTarget* dt);
     GrDrawTarget* getLastDrawTarget() { return fLastDrawTarget; }
 
-    static SampleConfig ComputeSampleConfig(const GrCaps& caps, int sampleCnt);
-
 protected:
-    GrRenderTarget(GrGpu* gpu, const GrSurfaceDesc& desc,
-                   SampleConfig sampleConfig, GrStencilAttachment* stencil = nullptr)
-        : INHERITED(gpu, desc)
-        , fStencilAttachment(stencil)
-        , fMultisampleSpecsID(0)
-        , fSampleConfig(sampleConfig)
-        , fLastDrawTarget(nullptr) {
-        fResolveRect.setLargestInverted();
-    }
+    enum class Flags {
+        kNone                = 0,
+        kMixedSampled        = 1 << 0,
+        kWindowRectsSupport  = 1 << 1
+    };
 
+    GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(Flags);
+
+    GrRenderTarget(GrGpu*, const GrSurfaceDesc&, Flags = Flags::kNone,
+                   GrStencilAttachment* = nullptr);
     ~GrRenderTarget() override;
 
     // override of GrResource
@@ -186,7 +146,7 @@
 
     GrStencilAttachment*  fStencilAttachment;
     uint8_t               fMultisampleSpecsID;
-    SampleConfig          fSampleConfig;
+    Flags                 fFlags;
 
     SkIRect               fResolveRect;
 
@@ -201,5 +161,6 @@
     typedef GrSurface INHERITED;
 };
 
+GR_MAKE_BITFIELD_CLASS_OPS(GrRenderTarget::Flags);
 
 #endif
diff --git a/include/gpu/GrTypes.h b/include/gpu/GrTypes.h
index a0f13aa..7bc949d 100644
--- a/include/gpu/GrTypes.h
+++ b/include/gpu/GrTypes.h
@@ -49,6 +49,27 @@
     \
     template <typename T> \
     friend X operator & (X a, T b); \
+
+/**
+ * Defines bitwise operators that make it possible to use an enum class as a
+ * very basic bitfield.
+ */
+#define GR_MAKE_BITFIELD_CLASS_OPS(X) \
+    inline X operator | (X a, X b) { \
+        return (X) ((int)a | (int)b); \
+    } \
+    inline X& operator |= (X& a, X b) { \
+        return (a = a | b); \
+    } \
+    inline bool operator & (X a, X b) { \
+        return SkToBool((int)a & (int)b); \
+    }
+
+#define GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(X) \
+    friend X operator | (X a, X b); \
+    friend X& operator |= (X& a, X b); \
+    friend bool operator & (X a, X b);
+
 ////////////////////////////////////////////////////////////////////////////////
 
 // compile time versions of min/max
diff --git a/include/private/GrInstancedPipelineInfo.h b/include/private/GrInstancedPipelineInfo.h
index f12b89f..7e6482d 100644
--- a/include/private/GrInstancedPipelineInfo.h
+++ b/include/private/GrInstancedPipelineInfo.h
@@ -17,7 +17,7 @@
 struct GrInstancedPipelineInfo {
     GrInstancedPipelineInfo(const GrRenderTarget* rt)
         : fIsMultisampled(rt->isStencilBufferMultisampled()),
-          fIsMixedSampled(rt->hasMixedSamples()),
+          fIsMixedSampled(rt->isMixedSampled()),
           fIsRenderingToFloat(GrPixelConfigIsFloatingPoint(rt->desc().fConfig)),
           fColorDisabled(false),
           fDrawingShapeToStencil(false),
diff --git a/include/private/GrRenderTargetProxy.h b/include/private/GrRenderTargetProxy.h
index 287aa01..e4bc70f 100644
--- a/include/private/GrRenderTargetProxy.h
+++ b/include/private/GrRenderTargetProxy.h
@@ -9,6 +9,7 @@
 #define GrRenderTargetProxy_DEFINED
 
 #include "GrRenderTarget.h"
+#include "GrRenderTargetPriv.h"
 #include "GrSurfaceProxy.h"
 #include "GrTypes.h"
 
@@ -25,7 +26,7 @@
      */
     static sk_sp<GrRenderTargetProxy> Make(const GrCaps&, const GrSurfaceDesc&,
                                            SkBackingFit, SkBudgeted);
-    static sk_sp<GrRenderTargetProxy> Make(sk_sp<GrRenderTarget> rt);
+    static sk_sp<GrRenderTargetProxy> Make(const GrCaps&, sk_sp<GrRenderTarget>);
 
     ~GrRenderTargetProxy() override;
 
@@ -36,78 +37,51 @@
     // Actually instantiate the backing rendertarget, if necessary.
     GrRenderTarget* instantiate(GrTextureProvider* texProvider);
 
-    /**
-     * @return true  if the surface is multisampled in all buffers,
-     *         false otherwise
-     */
-    bool isUnifiedMultisampled() const {
-        if (fSampleConfig != GrRenderTarget::kUnified_SampleConfig) {
-            return false;
-        }
-        return 0 != fDesc.fSampleCnt;
-    }
+    bool isStencilBufferMultisampled() const { return fDesc.fSampleCnt > 0; }
 
     /**
-     * @return true if the surface is multisampled in the stencil buffer,
-     *         false otherwise
+     * For our purposes, "Mixed Sampled" means the stencil buffer is multisampled but the color
+     * buffer is not.
      */
-    bool isStencilBufferMultisampled() const {
-        return 0 != fDesc.fSampleCnt;
-    }
+    bool isMixedSampled() const { return fFlags & GrRenderTargetPriv::Flags::kMixedSampled; }
 
     /**
-     * @return the number of color samples-per-pixel, or zero if non-MSAA or
-     *         multisampled in the stencil buffer only.
+     * "Unified Sampled" means the stencil and color buffers are both multisampled.
      */
-    int numColorSamples() const {
-        if (fSampleConfig == GrRenderTarget::kUnified_SampleConfig) {
-            return fDesc.fSampleCnt;
-        }
-        return 0;
-    }
+    bool isUnifiedMultisampled() const { return fDesc.fSampleCnt > 0 && !this->isMixedSampled(); }
 
     /**
-     * @return the number of stencil samples-per-pixel, or zero if non-MSAA.
+     * Returns the number of samples/pixel in the stencil buffer (Zero if non-MSAA).
      */
-    int numStencilSamples() const {
-        return fDesc.fSampleCnt;
-    }
+    int numStencilSamples() const { return fDesc.fSampleCnt; }
 
     /**
-     * @return true if the surface is mixed sampled, false otherwise.
+     * Returns the number of samples/pixel in the color buffer (Zero if non-MSAA or mixed sampled).
      */
-    bool hasMixedSamples() const {
-        SkASSERT(GrRenderTarget::kStencil_SampleConfig != fSampleConfig ||
-                 this->isStencilBufferMultisampled());
-        return GrRenderTarget::kStencil_SampleConfig == fSampleConfig;
-    }
+    int numColorSamples() const { return this->isMixedSampled() ? 0 : fDesc.fSampleCnt; }
 
     void setLastDrawTarget(GrDrawTarget* dt);
     GrDrawTarget* getLastDrawTarget() { return fLastDrawTarget; }
 
+    GrRenderTargetPriv::Flags testingOnly_getFlags() const;
+
 private:
-    // TODO: we can probably munge the 'desc' in both the wrapped and deferred 
-    // cases to make the sampleConfig/numSamples stuff more rational.
-    GrRenderTargetProxy(const GrCaps& caps, const GrSurfaceDesc& desc,
-                        SkBackingFit fit, SkBudgeted budgeted)
-        : INHERITED(desc, fit, budgeted)
-        , fTarget(nullptr)
-        , fSampleConfig(GrRenderTarget::ComputeSampleConfig(caps, desc.fSampleCnt))
-        , fLastDrawTarget(nullptr) {
-    }
+    // Deferred version
+    GrRenderTargetProxy(const GrCaps&, const GrSurfaceDesc&, SkBackingFit, SkBudgeted);
 
     // Wrapped version
-    GrRenderTargetProxy(sk_sp<GrRenderTarget> rt);
+    GrRenderTargetProxy(const GrCaps&, sk_sp<GrRenderTarget> rt);
 
     // For wrapped render targets we store it here.
     // For deferred proxies we will fill this in when we need to instantiate the deferred resource
-    sk_sp<GrRenderTarget>        fTarget;
+    sk_sp<GrRenderTarget>       fTarget;
 
-    // The sample config doesn't usually get computed until the render target is instantiated but
-    // the render target proxy may need to answer queries about it before then. For this reason
-    // we precompute it in the deferred case. In the wrapped case we just copy the wrapped
+    // These don't usually get computed until the render target is instantiated, but the render
+    // target proxy may need to answer queries about it before then. And since in the deferred case
+    // we know the newly created render target will be internal, we are able to precompute what the
+    // flags will ultimately end up being. In the wrapped case we just copy the wrapped
     // rendertarget's info here.
-    GrRenderTarget::SampleConfig fSampleConfig;
+    GrRenderTargetPriv::Flags   fFlags;
 
     // The last drawTarget that wrote to or is currently going to write to this renderTarget
     // The drawTarget can be closed (e.g., no draw context is currently bound
diff --git a/src/gpu/GrRenderTarget.cpp b/src/gpu/GrRenderTarget.cpp
index a0700a0..90f94b3 100644
--- a/src/gpu/GrRenderTarget.cpp
+++ b/src/gpu/GrRenderTarget.cpp
@@ -16,6 +16,18 @@
 #include "GrRenderTargetPriv.h"
 #include "GrStencilAttachment.h"
 
+GrRenderTarget::GrRenderTarget(GrGpu* gpu, const GrSurfaceDesc& desc, Flags flags,
+                               GrStencilAttachment* stencil)
+    : INHERITED(gpu, desc)
+    , fStencilAttachment(stencil)
+    , fMultisampleSpecsID(0)
+    , fFlags(flags)
+    , fLastDrawTarget(nullptr) {
+    SkASSERT(!(fFlags & Flags::kMixedSampled) || fDesc.fSampleCnt > 0);
+    SkASSERT(!(fFlags & Flags::kWindowRectsSupport) || gpu->caps()->maxWindowRectangles() > 0);
+    fResolveRect.setLargestInverted();
+}
+
 GrRenderTarget::~GrRenderTarget() {
     if (fLastDrawTarget) {
         fLastDrawTarget->clearRT();
@@ -115,11 +127,3 @@
 GrRenderTargetPriv::getMultisampleSpecs(const GrStencilSettings& stencil) const {
     return fRenderTarget->getGpu()->getMultisampleSpecs(fRenderTarget, stencil);
 }
-
-GrRenderTarget::SampleConfig GrRenderTarget::ComputeSampleConfig(const GrCaps& caps,
-                                                                 int sampleCnt) {
-    return (caps.usesMixedSamples() && sampleCnt > 0)
-                        ? GrRenderTarget::kStencil_SampleConfig
-                        : GrRenderTarget::kUnified_SampleConfig;
-}
-
diff --git a/src/gpu/GrRenderTargetPriv.h b/src/gpu/GrRenderTargetPriv.h
index db66bc3..2c828ec 100644
--- a/src/gpu/GrRenderTargetPriv.h
+++ b/src/gpu/GrRenderTargetPriv.h
@@ -35,7 +35,10 @@
     const GrGpu::MultisampleSpecs& getMultisampleSpecs(const GrStencilSettings& stencil) const;
     uint8_t& accessMultisampleSpecsID() { return fRenderTarget->fMultisampleSpecsID; }
 
-    GrRenderTarget::SampleConfig sampleConfig() const { return fRenderTarget->fSampleConfig; }
+    typedef GrRenderTarget::Flags Flags;
+
+    Flags flags() const { return fRenderTarget->fFlags; }
+    bool supportsWindowRectangles() const { return this->flags() & Flags::kWindowRectsSupport; }
 
 private:
     explicit GrRenderTargetPriv(GrRenderTarget* renderTarget) : fRenderTarget(renderTarget) {}
diff --git a/src/gpu/GrRenderTargetProxy.cpp b/src/gpu/GrRenderTargetProxy.cpp
index 6c422c0..3d6587f 100644
--- a/src/gpu/GrRenderTargetProxy.cpp
+++ b/src/gpu/GrRenderTargetProxy.cpp
@@ -7,14 +7,34 @@
 
 #include "GrRenderTargetProxy.h"
 
+#include "GrCaps.h"
 #include "GrDrawTarget.h"
 #include "GrGpuResourcePriv.h"
-#include "GrRenderTargetPriv.h"
 
-GrRenderTargetProxy::GrRenderTargetProxy(sk_sp<GrRenderTarget> rt)
+// Deferred version
+// TODO: we can probably munge the 'desc' in both the wrapped and deferred
+// cases to make the sampleConfig/numSamples stuff more rational.
+GrRenderTargetProxy::GrRenderTargetProxy(const GrCaps& caps, const GrSurfaceDesc& desc,
+                                         SkBackingFit fit, SkBudgeted budgeted)
+    : INHERITED(desc, fit, budgeted)
+    , fTarget(nullptr)
+    , fFlags(GrRenderTargetPriv::Flags::kNone)
+    , fLastDrawTarget(nullptr) {
+    // Since we know the newly created render target will be internal, we are able to precompute
+    // what the flags will ultimately end up being.
+    if (caps.usesMixedSamples() && fDesc.fSampleCnt > 0) {
+        fFlags |= GrRenderTargetPriv::Flags::kMixedSampled;
+    }
+    if (caps.maxWindowRectangles() > 0) {
+        fFlags |= GrRenderTargetPriv::Flags::kWindowRectsSupport;
+    }
+}
+
+// Wrapped version
+GrRenderTargetProxy::GrRenderTargetProxy(const GrCaps& caps, sk_sp<GrRenderTarget> rt)
     : INHERITED(rt->desc(), SkBackingFit::kExact, rt->resourcePriv().isBudgeted())
     , fTarget(std::move(rt))
-    , fSampleConfig(fTarget->renderTargetPriv().sampleConfig())
+    , fFlags(fTarget->renderTargetPriv().flags())
     , fLastDrawTarget(nullptr) {
 }
 
@@ -47,7 +67,7 @@
     fTarget = sk_ref_sp(tex->asRenderTarget());
 
     // Check that our a priori computation matched the ultimate reality
-    SkASSERT(fSampleConfig == fTarget->renderTargetPriv().sampleConfig());
+    SkASSERT(fFlags == fTarget->renderTargetPriv().flags());
 
     return fTarget.get();
 }
@@ -71,7 +91,7 @@
     return sk_sp<GrRenderTargetProxy>(new GrRenderTargetProxy(caps, desc, fit, budgeted));
 }
 
-sk_sp<GrRenderTargetProxy> GrRenderTargetProxy::Make(sk_sp<GrRenderTarget> rt) {
-    return sk_sp<GrRenderTargetProxy>(new GrRenderTargetProxy(rt));
+sk_sp<GrRenderTargetProxy> GrRenderTargetProxy::Make(const GrCaps& caps, sk_sp<GrRenderTarget> rt) {
+    return sk_sp<GrRenderTargetProxy>(new GrRenderTargetProxy(caps, rt));
 }
 
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index c91a0f4..1300bb7 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -722,7 +722,7 @@
     } else {
         idDesc.fRTFBOOwnership = GrBackendObjectOwnership::kBorrowed;
     }
-    idDesc.fSampleConfig = GrRenderTarget::kUnified_SampleConfig;
+    idDesc.fIsMixedSampled = false;
 
     GrSurfaceDesc desc;
     desc.fConfig = wrapDesc.fConfig;
@@ -1505,7 +1505,7 @@
     idDesc->fTexFBOID = 0;
     SkASSERT((GrGLCaps::kMixedSamples_MSFBOType == this->glCaps().msFBOType()) ==
              this->caps()->usesMixedSamples());
-    idDesc->fSampleConfig = GrRenderTarget::ComputeSampleConfig(*this->caps(), desc.fSampleCnt);
+    idDesc->fIsMixedSampled = desc.fSampleCnt > 0 && this->caps()->usesMixedSamples();
 
     GrGLenum status;
 
@@ -2971,7 +2971,7 @@
     }
 
     if (0 != this->caps()->maxRasterSamples()) {
-        if (useHWAA && rt->hasMixedSamples() && !stencilEnabled) {
+        if (useHWAA && rt->isMixedSampled() && !stencilEnabled) {
             // Since stencil is disabled and we want more samples than are in the color buffer, we
             // need to tell the rasterizer explicitly how many to run.
             if (kYes_TriState != fHWRasterMultisampleEnabled) {
@@ -2990,7 +2990,7 @@
             }
         }
     } else {
-        SkASSERT(!useHWAA || !rt->hasMixedSamples() || stencilEnabled);
+        SkASSERT(!useHWAA || !rt->isMixedSampled() || stencilEnabled);
     }
 }
 
@@ -4440,7 +4440,7 @@
 
 void GrGLGpu::onGetMultisampleSpecs(GrRenderTarget* rt, const GrStencilSettings& stencil,
                                     int* effectiveSampleCnt, SamplePattern* samplePattern) {
-    SkASSERT(!rt->hasMixedSamples() || rt->renderTargetPriv().getStencilAttachment() ||
+    SkASSERT(!rt->isMixedSampled() || rt->renderTargetPriv().getStencilAttachment() ||
              stencil.isDisabled());
 
     this->flushStencil(stencil);
diff --git a/src/gpu/gl/GrGLRenderTarget.cpp b/src/gpu/gl/GrGLRenderTarget.cpp
index 27713b2..8dacf48 100644
--- a/src/gpu/gl/GrGLRenderTarget.cpp
+++ b/src/gpu/gl/GrGLRenderTarget.cpp
@@ -23,7 +23,7 @@
                                    const IDDesc& idDesc,
                                    GrGLStencilAttachment* stencil)
     : GrSurface(gpu, desc)
-    , INHERITED(gpu, desc, idDesc.fSampleConfig, stencil) {
+    , INHERITED(gpu, desc, ComputeFlags(gpu->glCaps(), idDesc), stencil) {
     this->init(desc, idDesc);
     this->registerWithCacheWrapped();
 }
@@ -31,10 +31,23 @@
 GrGLRenderTarget::GrGLRenderTarget(GrGLGpu* gpu, const GrSurfaceDesc& desc,
                                    const IDDesc& idDesc)
     : GrSurface(gpu, desc)
-    , INHERITED(gpu, desc, idDesc.fSampleConfig) {
+    , INHERITED(gpu, desc, ComputeFlags(gpu->glCaps(), idDesc)) {
     this->init(desc, idDesc);
 }
 
+inline GrRenderTarget::Flags GrGLRenderTarget::ComputeFlags(const GrGLCaps& glCaps,
+                                                            const IDDesc& idDesc) {
+    Flags flags = Flags::kNone;
+    if (idDesc.fIsMixedSampled) {
+        SkASSERT(glCaps.usesMixedSamples() && idDesc.fRTFBOID); // FBO 0 can't be mixed sampled.
+        flags |= Flags::kMixedSampled;
+    }
+    if (glCaps.maxWindowRectangles() > 0 && idDesc.fRTFBOID) {
+        flags |= Flags::kWindowRectsSupport;
+    }
+    return flags;
+}
+
 void GrGLRenderTarget::init(const GrSurfaceDesc& desc, const IDDesc& idDesc) {
     fRTFBOID                = idDesc.fRTFBOID;
     fTexFBOID               = idDesc.fTexFBOID;
diff --git a/src/gpu/gl/GrGLRenderTarget.h b/src/gpu/gl/GrGLRenderTarget.h
index edf35a1..85e377f 100644
--- a/src/gpu/gl/GrGLRenderTarget.h
+++ b/src/gpu/gl/GrGLRenderTarget.h
@@ -13,6 +13,7 @@
 #include "GrRenderTarget.h"
 #include "SkScalar.h"
 
+class GrGLCaps;
 class GrGLGpu;
 class GrGLStencilAttachment;
 
@@ -23,11 +24,11 @@
     enum { kUnresolvableFBOID = 0 };
 
     struct IDDesc {
-        GrGLuint                     fRTFBOID;
-        GrBackendObjectOwnership     fRTFBOOwnership;
-        GrGLuint                     fTexFBOID;
-        GrGLuint                     fMSColorRenderbufferID;
-        GrRenderTarget::SampleConfig fSampleConfig;
+        GrGLuint                   fRTFBOID;
+        GrBackendObjectOwnership   fRTFBOOwnership;
+        GrGLuint                   fTexFBOID;
+        GrGLuint                   fMSColorRenderbufferID;
+        bool                       fIsMixedSampled;
     };
 
     static GrGLRenderTarget* CreateWrapped(GrGLGpu*,
@@ -83,6 +84,8 @@
     // Constructor for instances wrapping backend objects.
     GrGLRenderTarget(GrGLGpu*, const GrSurfaceDesc&, const IDDesc&, GrGLStencilAttachment*);
 
+    static Flags ComputeFlags(const GrGLCaps&, const IDDesc&);
+
     GrGLGpu* getGLGpu() const;
     bool completeStencilAttachment() override;
 
diff --git a/src/gpu/vk/GrVkRenderTarget.cpp b/src/gpu/vk/GrVkRenderTarget.cpp
index 83a4b44..dc966ca 100644
--- a/src/gpu/vk/GrVkRenderTarget.cpp
+++ b/src/gpu/vk/GrVkRenderTarget.cpp
@@ -32,7 +32,7 @@
     : GrSurface(gpu, desc)
     , GrVkImage(info, wrapped)
     // for the moment we only support 1:1 color to stencil
-    , GrRenderTarget(gpu, desc, kUnified_SampleConfig)
+    , GrRenderTarget(gpu, desc)
     , fColorAttachmentView(colorAttachmentView)
     , fMSAAImage(new GrVkImage(msaaInfo, GrVkImage::kNot_Wrapped))
     , fResolveAttachmentView(resolveAttachmentView)
@@ -57,7 +57,7 @@
     : GrSurface(gpu, desc)
     , GrVkImage(info, wrapped)
     // for the moment we only support 1:1 color to stencil
-    , GrRenderTarget(gpu, desc, kUnified_SampleConfig)
+    , GrRenderTarget(gpu, desc)
     , fColorAttachmentView(colorAttachmentView)
     , fMSAAImage(new GrVkImage(msaaInfo, GrVkImage::kNot_Wrapped))
     , fResolveAttachmentView(resolveAttachmentView)
@@ -79,7 +79,7 @@
                                    GrVkImage::Wrapped wrapped)
     : GrSurface(gpu, desc)
     , GrVkImage(info, wrapped)
-    , GrRenderTarget(gpu, desc, kUnified_SampleConfig)
+    , GrRenderTarget(gpu, desc)
     , fColorAttachmentView(colorAttachmentView)
     , fMSAAImage(nullptr)
     , fResolveAttachmentView(nullptr)
@@ -100,7 +100,7 @@
                                    GrVkImage::Wrapped wrapped)
     : GrSurface(gpu, desc)
     , GrVkImage(info, wrapped)
-    , GrRenderTarget(gpu, desc, kUnified_SampleConfig)
+    , GrRenderTarget(gpu, desc)
     , fColorAttachmentView(colorAttachmentView)
     , fMSAAImage(nullptr)
     , fResolveAttachmentView(nullptr)
diff --git a/tests/ProxyTest.cpp b/tests/ProxyTest.cpp
index 2ed4591..45d3945 100644
--- a/tests/ProxyTest.cpp
+++ b/tests/ProxyTest.cpp
@@ -10,6 +10,7 @@
 #include "Test.h"
 
 #if SK_SUPPORT_GPU
+#include "GrGpu.h"
 #include "GrSurfaceProxy.h"
 #include "GrTextureProxy.h"
 #include "GrRenderTargetProxy.h"
@@ -50,7 +51,8 @@
                               rtProxy->isStencilBufferMultisampled());
     REPORTER_ASSERT(reporter, rt->numColorSamples() == rtProxy->numColorSamples());
     REPORTER_ASSERT(reporter, rt->numStencilSamples() == rtProxy->numStencilSamples());
-    REPORTER_ASSERT(reporter, rt->hasMixedSamples() == rtProxy->hasMixedSamples());
+    REPORTER_ASSERT(reporter, rt->isMixedSampled() == rtProxy->isMixedSampled());
+    REPORTER_ASSERT(reporter, rt->renderTargetPriv().flags() == rtProxy->testingOnly_getFlags());
 }
 
 static void check_texture(skiatest::Reporter* reporter,
@@ -124,6 +126,7 @@
 
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WrappedProxyTest, reporter, ctxInfo) {
     GrTextureProvider* provider = ctxInfo.grContext()->textureProvider();
+    const GrCaps& caps = *ctxInfo.grContext()->caps();
 
     static const int kWidthHeight = 100;
 
@@ -131,8 +134,7 @@
         for (auto config : { kAlpha_8_GrPixelConfig, kRGBA_8888_GrPixelConfig }) {
             for (auto budgeted : { SkBudgeted::kYes, SkBudgeted::kNo }) {
                 for (auto numSamples: { 0, 4}) {
-                    bool renderable = ctxInfo.grContext()->caps()->isConfigRenderable(
-                                                                config, numSamples > 0);
+                    bool renderable = caps.isConfigRenderable(config, numSamples > 0);
 
                     GrSurfaceDesc desc;
                     desc.fOrigin = origin;
@@ -141,14 +143,40 @@
                     desc.fConfig = config;
                     desc.fSampleCnt = numSamples;
 
+                    // External on-screen render target.
+                    if (renderable && kOpenGL_GrBackend == ctxInfo.backend()) {
+                        GrBackendRenderTargetDesc backendDesc;
+                        backendDesc.fWidth = kWidthHeight;
+                        backendDesc.fHeight = kWidthHeight;
+                        backendDesc.fConfig = config;
+                        backendDesc.fOrigin = origin;
+                        backendDesc.fSampleCnt = numSamples;
+                        backendDesc.fStencilBits = 8;
+                        backendDesc.fRenderTargetHandle = 0;
+
+                        GrGpu* gpu = ctxInfo.grContext()->getGpu();
+                        sk_sp<GrRenderTarget> defaultFBO(
+                            gpu->wrapBackendRenderTarget(backendDesc, kBorrow_GrWrapOwnership));
+                        SkASSERT(!defaultFBO->renderTargetPriv().supportsWindowRectangles());
+
+                        sk_sp<GrRenderTargetProxy> rtProxy(
+                            GrRenderTargetProxy::Make(caps, defaultFBO));
+                        check_surface(reporter, rtProxy.get(), origin,
+                                      kWidthHeight, kWidthHeight, config);
+                        check_rendertarget(reporter, provider, rtProxy.get(), SkBackingFit::kExact);
+                    }
+
                     sk_sp<GrTexture> tex;
 
+                    // Internal offscreen render target.
                     if (renderable) {
                         desc.fFlags = kRenderTarget_GrSurfaceFlag;
                         tex.reset(provider->createTexture(desc, budgeted));
                         sk_sp<GrRenderTarget> rt(sk_ref_sp(tex->asRenderTarget()));
+                        SkASSERT(caps.maxWindowRectangles() <= 0 ||
+                                rt->renderTargetPriv().supportsWindowRectangles());
 
-                        sk_sp<GrRenderTargetProxy> rtProxy(GrRenderTargetProxy::Make(rt));
+                        sk_sp<GrRenderTargetProxy> rtProxy(GrRenderTargetProxy::Make(caps, rt));
                         check_surface(reporter, rtProxy.get(), origin,
                                       kWidthHeight, kWidthHeight, config);
                         check_rendertarget(reporter, provider, rtProxy.get(), SkBackingFit::kExact);
diff --git a/tools/gpu/GrTest.cpp b/tools/gpu/GrTest.cpp
index 33d8844..d2c7295 100644
--- a/tools/gpu/GrTest.cpp
+++ b/tools/gpu/GrTest.cpp
@@ -13,6 +13,7 @@
 #include "GrDrawingManager.h"
 #include "GrGpuResourceCacheAccess.h"
 #include "GrPipelineBuilder.h"
+#include "GrRenderTargetProxy.h"
 #include "GrResourceCache.h"
 
 #include "SkGpuDevice.h"
@@ -258,6 +259,12 @@
 #undef RETURN_IF_ABANDONED
 
 ///////////////////////////////////////////////////////////////////////////////
+
+GrRenderTargetPriv::Flags GrRenderTargetProxy::testingOnly_getFlags() const {
+    return fFlags;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Code for the mock context. It's built on a mock GrGpu class that does nothing.
 ////