Use mixed samples internally for default coverage AA

Lays the infrastructure to use mixed samples internally, and begins
using nvpr with mixed samples on the default "gl" and "gles" configs.

In this rendition, we take the simplest approach possible re: stencil
attachments. We initially create a render target without stencil
(i.e., 0 samples). Then, any time a proxy needs a stencil buffer with
more samples than its target currently has, we create and attach a new
stencil buffer. However, we never "downgrade" a render target's
stencil attachment to one with fewer samples. So if the proxy only
needs one sample and the target has many, we leave it.

Bug: skia:
Change-Id: I8558ba799ac3dee457f349f77d4517c11413c9a9
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/224456
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp
index 7399076..30f86da 100644
--- a/src/gpu/GrClipStackClip.cpp
+++ b/src/gpu/GrClipStackClip.cpp
@@ -125,6 +125,7 @@
         GrShape shape(path, GrStyle::SimpleFill());
         GrPathRenderer::CanDrawPathArgs canDrawArgs;
         canDrawArgs.fCaps = context->priv().caps();
+        canDrawArgs.fProxy = renderTargetContext->proxy();
         canDrawArgs.fClipConservativeBounds = &scissorRect;
         canDrawArgs.fViewMatrix = &viewMatrix;
         canDrawArgs.fShape = &shape;
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index e4a0fdf..793339e 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -425,7 +425,9 @@
 
 int GrGpu::findOrAssignSamplePatternKey(GrRenderTarget* renderTarget) {
     SkASSERT(this->caps()->sampleLocationsSupport());
-    SkASSERT(renderTarget->numSamples() > 1);
+    SkASSERT(renderTarget->numSamples() > 1 ||
+             (renderTarget->renderTargetPriv().getStencilAttachment() &&
+              renderTarget->renderTargetPriv().getStencilAttachment()->numSamples() > 1));
 
     SkSTArray<16, SkPoint> sampleLocations;
     this->querySampleLocations(renderTarget, &sampleLocations);
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 10fa09e..2897445 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -431,9 +431,8 @@
     // width and height may be larger than rt (if underlying API allows it).
     // Returns nullptr if compatible sb could not be created, otherwise the caller owns the ref on
     // the GrStencilAttachment.
-    virtual GrStencilAttachment* createStencilAttachmentForRenderTarget(const GrRenderTarget*,
-                                                                        int width,
-                                                                        int height) = 0;
+    virtual GrStencilAttachment* createStencilAttachmentForRenderTarget(
+            const GrRenderTarget*, int width, int height, int numStencilSamples) = 0;
 
     // Determines whether a texture will need to be rescaled in order to be used with the
     // GrSamplerState.
diff --git a/src/gpu/GrOpList.cpp b/src/gpu/GrOpList.cpp
index 9a9ff14..ae16aa6 100644
--- a/src/gpu/GrOpList.cpp
+++ b/src/gpu/GrOpList.cpp
@@ -11,6 +11,7 @@
 #include "src/gpu/GrDeferredProxyUploader.h"
 #include "src/gpu/GrMemoryPool.h"
 #include "src/gpu/GrRenderTargetPriv.h"
+#include "src/gpu/GrStencilAttachment.h"
 #include "src/gpu/GrSurfaceProxy.h"
 #include "src/gpu/GrTextureProxyPriv.h"
 #include <atomic>
@@ -161,16 +162,19 @@
         return false;
     }
 
-    bool needsStencil = fTarget->asRenderTargetProxy()
-                                        ? fTarget->asRenderTargetProxy()->needsStencil()
-                                        : false;
+    int minStencilSampleCount = (fTarget->asRenderTargetProxy())
+            ? fTarget->asRenderTargetProxy()->numStencilSamples()
+            : 0;
 
-    if (needsStencil) {
+    if (minStencilSampleCount) {
         GrRenderTarget* rt = fTarget->peekRenderTarget();
+        SkASSERT(rt);
 
-        if (!rt->renderTargetPriv().getStencilAttachment()) {
+        GrStencilAttachment* stencil = rt->renderTargetPriv().getStencilAttachment();
+        if (!stencil) {
             return false;
         }
+        SkASSERT(stencil->numSamples() >= minStencilSampleCount);
     }
 
     GrSurface* surface = fTarget->peekSurface();
diff --git a/src/gpu/GrPathRenderer.cpp b/src/gpu/GrPathRenderer.cpp
index 93a4d55..16d98db 100644
--- a/src/gpu/GrPathRenderer.cpp
+++ b/src/gpu/GrPathRenderer.cpp
@@ -45,6 +45,7 @@
     args.validate();
     CanDrawPathArgs canArgs;
     canArgs.fCaps = args.fContext->priv().caps();
+    canArgs.fProxy = args.fRenderTargetContext->proxy();
     canArgs.fClipConservativeBounds = args.fClipConservativeBounds;
     canArgs.fViewMatrix = args.fViewMatrix;
     canArgs.fShape = args.fShape;
diff --git a/src/gpu/GrPathRenderer.h b/src/gpu/GrPathRenderer.h
index dfe7a71..f73e43c 100644
--- a/src/gpu/GrPathRenderer.h
+++ b/src/gpu/GrPathRenderer.h
@@ -19,6 +19,7 @@
 class GrPaint;
 class GrRecordingContext;
 class GrRenderTargetContext;
+class GrRenderTargetProxy;
 class GrShape;
 class GrStyle;
 struct GrUserStencilSettings;
@@ -76,6 +77,7 @@
         SkDEBUGCODE(CanDrawPathArgs() { memset(this, 0, sizeof(*this)); }) // For validation.
 
         const GrCaps*               fCaps;
+        const GrRenderTargetProxy*  fProxy;
         const SkIRect*              fClipConservativeBounds;
         const SkMatrix*             fViewMatrix;
         const GrShape*              fShape;
@@ -88,6 +90,7 @@
 #ifdef SK_DEBUG
         void validate() const {
             SkASSERT(fCaps);
+            SkASSERT(fProxy);
             SkASSERT(fClipConservativeBounds);
             SkASSERT(fViewMatrix);
             SkASSERT(fShape);
diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp
index 8f297d8..99f3c24 100644
--- a/src/gpu/GrReducedClip.cpp
+++ b/src/gpu/GrReducedClip.cpp
@@ -866,6 +866,7 @@
             GrShape shape(clipPath, GrStyle::SimpleFill());
             GrPathRenderer::CanDrawPathArgs canDrawArgs;
             canDrawArgs.fCaps = context->priv().caps();
+            canDrawArgs.fProxy = renderTargetContext->proxy();
             canDrawArgs.fClipConservativeBounds = &stencilClip.fixedClip().scissorRect();
             canDrawArgs.fViewMatrix = &SkMatrix::I();
             canDrawArgs.fShape = &shape;
diff --git a/src/gpu/GrRenderTarget.cpp b/src/gpu/GrRenderTarget.cpp
index 3642ec3..6c7a893 100644
--- a/src/gpu/GrRenderTarget.cpp
+++ b/src/gpu/GrRenderTarget.cpp
@@ -74,11 +74,27 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void GrRenderTargetPriv::attachStencilAttachment(sk_sp<GrStencilAttachment> stencil) {
+#ifdef SK_DEBUG
+    if (1 == fRenderTarget->fSampleCnt) {
+        // TODO: We don't expect a mixed sampled render target to ever change its stencil buffer
+        // right now. But if it does swap in a stencil buffer with a different number of samples,
+        // and if we have a valid fSamplePatternKey, we will need to invalidate fSamplePatternKey
+        // here and add tests to make sure we it properly.
+        SkASSERT(GrSamplePatternDictionary::kInvalidSamplePatternKey ==
+                 fRenderTarget->fSamplePatternKey);
+    } else {
+        // Render targets with >1 color sample should never use mixed samples. (This would lead to
+        // different sample patterns, depending on stencil state.)
+        SkASSERT(!stencil || stencil->numSamples() == fRenderTarget->fSampleCnt);
+    }
+#endif
+
     if (!stencil && !fRenderTarget->fStencilAttachment) {
         // No need to do any work since we currently don't have a stencil attachment and
         // we're not actually adding one.
         return;
     }
+
     fRenderTarget->fStencilAttachment = std::move(stencil);
     if (!fRenderTarget->completeStencilAttachment()) {
         fRenderTarget->fStencilAttachment = nullptr;
@@ -91,7 +107,19 @@
 }
 
 int GrRenderTargetPriv::getSamplePatternKey() const {
-    SkASSERT(fRenderTarget->fSampleCnt > 1);
+#ifdef SK_DEBUG
+    GrStencilAttachment* stencil = fRenderTarget->fStencilAttachment.get();
+    if (fRenderTarget->fSampleCnt <= 1) {
+        // If the color buffer is not multisampled, the sample pattern better come from the stencil
+        // buffer (mixed samples).
+        SkASSERT(stencil && stencil->numSamples() > 1);
+    } else {
+        // The color sample count and stencil count cannot both be unequal and both greater than
+        // one. If this were the case, there would be more than one sample pattern associated with
+        // the render target.
+        SkASSERT(!stencil || stencil->numSamples() == fRenderTarget->fSampleCnt);
+    }
+#endif
     if (GrSamplePatternDictionary::kInvalidSamplePatternKey == fRenderTarget->fSamplePatternKey) {
         fRenderTarget->fSamplePatternKey =
                 fRenderTarget->getGpu()->findOrAssignSamplePatternKey(fRenderTarget);
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index f8254b2..fb9bd38 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -35,7 +35,6 @@
 #include "src/gpu/GrPathRenderer.h"
 #include "src/gpu/GrRecordingContextPriv.h"
 #include "src/gpu/GrRenderTargetContextPriv.h"
-#include "src/gpu/GrRenderTargetProxyPriv.h"
 #include "src/gpu/GrResourceProvider.h"
 #include "src/gpu/GrStencilAttachment.h"
 #include "src/gpu/GrStyle.h"
@@ -820,16 +819,28 @@
     // Although the clear will ignore the stencil buffer, following draw ops may not so we can't get
     // rid of all the preceding ops. Beware! If we ever add any ops that have a side effect beyond
     // modifying the stencil buffer we will need a more elaborate tracking system (skbug.com/7002).
-    return GrRenderTargetOpList::CanDiscardPreviousOps(!fNeedsStencil);
+    return GrRenderTargetOpList::CanDiscardPreviousOps(!fNumStencilSamples);
 }
 
-void GrRenderTargetContext::setNeedsStencil() {
-    // Don't clear stencil until after we've changed fNeedsStencil. This ensures we don't loop
+void GrRenderTargetContext::setNeedsStencil(bool multisampled) {
+    // Don't clear stencil until after we've changed fNumStencilSamples. This ensures we don't loop
     // forever in the event that there are driver bugs and we need to clear as a draw.
-    bool needsStencilClear = !fNeedsStencil;
+    bool needsStencilClear = !fNumStencilSamples;
 
-    fNeedsStencil = true;
-    fRenderTargetProxy->setNeedsStencil();
+    int numRequiredSamples = this->numSamples();
+    if (multisampled && 1 == numRequiredSamples) {
+        // The caller has requested a multisampled stencil buffer on a non-MSAA render target. Use
+        // mixed samples.
+        SkASSERT(fRenderTargetProxy->canUseMixedSamples(*this->caps()));
+        numRequiredSamples = this->caps()->internalMultisampleCount(
+                this->colorSpaceInfo().config());
+    }
+    SkASSERT(numRequiredSamples > 0);
+
+    if (numRequiredSamples > fNumStencilSamples) {
+        fNumStencilSamples = numRequiredSamples;
+        fRenderTargetProxy->setNeedsStencil(fNumStencilSamples);
+    }
 
     if (needsStencilClear) {
         if (this->caps()->performStencilClearsAsDraws()) {
@@ -903,7 +914,6 @@
         return;
     }
 
-
     std::unique_ptr<GrOp> op = GrStencilPathOp::Make(fRenderTargetContext->fContext,
                                                      viewMatrix,
                                                      GrAA::kYes == doStencilMSAA,
@@ -916,7 +926,7 @@
     }
     op->setClippedBounds(bounds);
 
-    fRenderTargetContext->setNeedsStencil();
+    fRenderTargetContext->setNeedsStencil(GrAA::kYes == doStencilMSAA);
     fRenderTargetContext->getRTOpList()->addOp(std::move(op), *fRenderTargetContext->caps());
 }
 
@@ -2420,6 +2430,7 @@
     GrShape shape(path, GrStyle::SimpleFill());
     GrPathRenderer::CanDrawPathArgs canDrawArgs;
     canDrawArgs.fCaps = fRenderTargetContext->caps();
+    canDrawArgs.fProxy = fRenderTargetContext->proxy();
     canDrawArgs.fViewMatrix = &viewMatrix;
     canDrawArgs.fShape = &shape;
     canDrawArgs.fClipConservativeBounds = &clipConservativeBounds;
@@ -2485,6 +2496,7 @@
 
     GrPathRenderer::CanDrawPathArgs canDrawArgs;
     canDrawArgs.fCaps = this->caps();
+    canDrawArgs.fProxy = this->proxy();
     canDrawArgs.fViewMatrix = &viewMatrix;
     canDrawArgs.fShape = &originalShape;
     canDrawArgs.fClipConservativeBounds = &clipConservativeBounds;
@@ -2593,7 +2605,7 @@
     bool usesStencil = fixedFunctionFlags & GrDrawOp::FixedFunctionFlags::kUsesStencil;
 
     if (usesStencil) {
-        this->setNeedsStencil();
+        this->setNeedsStencil(usesHWAA);
     }
 
     if (!clip.apply(fContext, this, usesHWAA, usesStencil, &appliedClip, &bounds)) {
@@ -2601,12 +2613,18 @@
         return;
     }
 
-    SkASSERT((!usesStencil && !appliedClip.hasStencilClip()) || fNeedsStencil);
+    SkASSERT((!usesStencil && !appliedClip.hasStencilClip()) || (fNumStencilSamples > 0));
 
     GrClampType clampType = GrPixelConfigClampType(this->colorSpaceInfo().config());
-    // MIXED SAMPLES TODO: check stencil buffer is MSAA and make sure stencil test is actually doing
-    // something (either in the clip or in the op).
-    bool hasMixedSampledCoverage = false;
+    // MIXED SAMPLES TODO: If we start using mixed samples for clips we will need to check the clip
+    // here as well.
+    bool hasMixedSampledCoverage = (usesHWAA && this->numSamples() <= 1);
+#ifdef SK_DEBUG
+    if (hasMixedSampledCoverage) {
+        SkASSERT(usesStencil);
+        SkASSERT(fRenderTargetProxy->canUseMixedSamples(*this->caps()));
+    }
+#endif
     GrProcessorSet::Analysis analysis = op->finalize(
             *this->caps(), &appliedClip, hasMixedSampledCoverage, clampType);
 
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index 2746fc5..07348b9 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -456,6 +456,7 @@
     void insertEventMarker(const SkString&);
 
     const GrCaps* caps() const;
+    const GrRenderTargetProxy* proxy() const { return fRenderTargetProxy.get(); }
     int width() const { return fRenderTargetProxy->width(); }
     int height() const { return fRenderTargetProxy->height(); }
     int numSamples() const { return fRenderTargetProxy->numSamples(); }
@@ -529,7 +530,7 @@
                              sk_sp<GrTextureProxy>);
 
     GrRenderTargetOpList::CanDiscardPreviousOps canDiscardPreviousOpsOnFullClear() const;
-    void setNeedsStencil();
+    void setNeedsStencil(bool multisampled);
 
     void internalClear(const GrFixedClip&, const SkPMColor4f&, CanClearFullscreen);
     void internalStencilClear(const GrFixedClip&, bool insideStencilMask);
@@ -627,7 +628,7 @@
     SkSurfaceProps fSurfaceProps;
     bool fManagedOpList;
 
-    bool fNeedsStencil = false;
+    int fNumStencilSamples = 0;
 #if GR_TEST_UTILS
     bool fPreserveOpsOnFullClear_TestingOnly = false;
 #endif
diff --git a/src/gpu/GrRenderTargetProxy.cpp b/src/gpu/GrRenderTargetProxy.cpp
index fc41221..20c1abf 100644
--- a/src/gpu/GrRenderTargetProxy.cpp
+++ b/src/gpu/GrRenderTargetProxy.cpp
@@ -26,9 +26,8 @@
                                          SkBudgeted budgeted, GrInternalSurfaceFlags surfaceFlags)
         : INHERITED(format, desc, origin, textureSwizzle, fit, budgeted, surfaceFlags)
         , fSampleCnt(desc.fSampleCnt)
-        , fOutputSwizzle(outputSwizzle)
-        , fNeedsStencil(false)
-        , fWrapsVkSecondaryCB(WrapsVkSecondaryCB::kNo) {
+        , fWrapsVkSecondaryCB(WrapsVkSecondaryCB::kNo)
+        , fOutputSwizzle(outputSwizzle) {
 }
 
 // Lazy-callback version
@@ -42,9 +41,8 @@
         : INHERITED(std::move(callback), lazyType, format, desc, origin, textureSwizzle, fit,
                     budgeted, surfaceFlags)
         , fSampleCnt(desc.fSampleCnt)
-        , fOutputSwizzle(outputSwizzle)
-        , fNeedsStencil(false)
-        , fWrapsVkSecondaryCB(wrapsVkSecondaryCB) {
+        , fWrapsVkSecondaryCB(wrapsVkSecondaryCB)
+        , fOutputSwizzle(outputSwizzle) {
     SkASSERT(SkToBool(kRenderTarget_GrSurfaceFlag & desc.fFlags));
 }
 
@@ -55,9 +53,8 @@
                                          WrapsVkSecondaryCB wrapsVkSecondaryCB)
         : INHERITED(std::move(surf), origin, textureSwizzle, SkBackingFit::kExact)
         , fSampleCnt(fTarget->asRenderTarget()->numSamples())
-        , fOutputSwizzle(outputSwizzle)
-        , fNeedsStencil(false)
-        , fWrapsVkSecondaryCB(wrapsVkSecondaryCB) {
+        , fWrapsVkSecondaryCB(wrapsVkSecondaryCB)
+        , fOutputSwizzle(outputSwizzle) {
 }
 
 int GrRenderTargetProxy::maxWindowRectangles(const GrCaps& caps) const {
@@ -70,7 +67,7 @@
     }
     static constexpr GrSurfaceDescFlags kDescFlags = kRenderTarget_GrSurfaceFlag;
 
-    if (!this->instantiateImpl(resourceProvider, fSampleCnt, fNeedsStencil, kDescFlags,
+    if (!this->instantiateImpl(resourceProvider, fSampleCnt, fNumStencilSamples, kDescFlags,
                                GrMipMapped::kNo, nullptr)) {
         return false;
     }
@@ -83,8 +80,8 @@
 sk_sp<GrSurface> GrRenderTargetProxy::createSurface(GrResourceProvider* resourceProvider) const {
     static constexpr GrSurfaceDescFlags kDescFlags = kRenderTarget_GrSurfaceFlag;
 
-    sk_sp<GrSurface> surface = this->createSurfaceImpl(resourceProvider, fSampleCnt, fNeedsStencil,
-                                                       kDescFlags, GrMipMapped::kNo);
+    sk_sp<GrSurface> surface = this->createSurfaceImpl(
+            resourceProvider, fSampleCnt, fNumStencilSamples, kDescFlags, GrMipMapped::kNo);
     if (!surface) {
         return nullptr;
     }
diff --git a/src/gpu/GrRenderTargetProxy.h b/src/gpu/GrRenderTargetProxy.h
index 77ad84d..9f1eb4b 100644
--- a/src/gpu/GrRenderTargetProxy.h
+++ b/src/gpu/GrRenderTargetProxy.h
@@ -9,6 +9,7 @@
 #define GrRenderTargetProxy_DEFINED
 
 #include "include/private/GrTypesPriv.h"
+#include "src/gpu/GrCaps.h"
 #include "src/gpu/GrSurfaceProxy.h"
 #include "src/gpu/GrSwizzle.h"
 
@@ -27,11 +28,29 @@
     // Actually instantiate the backing rendertarget, if necessary.
     bool instantiate(GrResourceProvider*) override;
 
+    bool canUseMixedSamples(const GrCaps& caps) const {
+        // TODO: Is this a wrapped external RT? Is GrBackendObjectOwnership available?
+        return caps.mixedSamplesSupport() && !this->glRTFBOIDIs0() &&
+               caps.internalMultisampleCount(this->config()) > 0;
+    }
+
     /*
-     * When instantiated does this proxy require a stencil buffer?
+     * Indicate that a draw to this proxy requires stencil, and how many stencil samples it needs.
+     * The number of stencil samples on this proxy will be equal to the largest sample count passed
+     * to this method.
      */
-    void setNeedsStencil() { fNeedsStencil = true; }
-    bool needsStencil() const { return fNeedsStencil; }
+    void setNeedsStencil(int8_t numStencilSamples) {
+        SkASSERT(numStencilSamples >= fSampleCnt);
+        fNumStencilSamples = SkTMax(numStencilSamples, fNumStencilSamples);
+    }
+
+    /**
+     * Returns the number of stencil samples required by this proxy.
+     * NOTE: Once instantiated, the actual render target may have more samples, but it is guaranteed
+     * to have at least this many. (After a multisample stencil buffer has been attached to a render
+     * target, we never "downgrade" it to one with fewer samples.)
+     */
+    int numStencilSamples() const { return fNumStencilSamples; }
 
     /**
      * Returns the number of samples/pixel in the color buffer (One if non-MSAA).
@@ -105,10 +124,10 @@
     // in the constructors, and always looks for the full 16 byte alignment, even if the fields in
     // that particular class don't require it. Changing the size of this object can move the start
     // address of other types, leading to this problem.
-    int                fSampleCnt;
-    GrSwizzle          fOutputSwizzle;
-    bool               fNeedsStencil;
+    int8_t             fSampleCnt;
+    int8_t             fNumStencilSamples = 0;
     WrapsVkSecondaryCB fWrapsVkSecondaryCB;
+    GrSwizzle          fOutputSwizzle;
     // This is to fix issue in large comment above. Without the padding we end 6 bytes into a 16
     // byte range, so the GrTextureProxy ends up starting 8 byte aligned by not 16. We add the
     // padding here to get us right up to the 16 byte alignment (technically any padding of 3-10
diff --git a/src/gpu/GrResourceAllocator.cpp b/src/gpu/GrResourceAllocator.cpp
index 4c83509..f73f5be 100644
--- a/src/gpu/GrResourceAllocator.cpp
+++ b/src/gpu/GrResourceAllocator.cpp
@@ -75,17 +75,19 @@
                                       ActualUse actualUse
                                       SkDEBUGCODE(, bool isDirectDstRead)) {
 
-    bool needsStencil = proxy->asRenderTargetProxy()
-                                        ? proxy->asRenderTargetProxy()->needsStencil()
-                                        : false;
-
     if (proxy->canSkipResourceAllocator()) {
-        if (needsStencil && proxy->isInstantiated()) {
-            // If the proxy is still not instantiated at this point but will need stencil, it will
-            // attach its own stencil buffer upon onFlush instantiation.
-            if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(
-                    fResourceProvider, proxy->peekSurface(), true /*needsStencil*/)) {
-                SkDebugf("WARNING: failed to attach stencil buffer. Rendering may be incorrect.\n");
+        // If the proxy is still not instantiated at this point but will need stencil, it will
+        // attach its own stencil buffer upon onFlush instantiation.
+        if (proxy->isInstantiated()) {
+            int minStencilSampleCount = (proxy->asRenderTargetProxy())
+                    ? proxy->asRenderTargetProxy()->numStencilSamples()
+                    : 0;
+            if (minStencilSampleCount) {
+                if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(
+                        fResourceProvider, proxy->peekSurface(), minStencilSampleCount)) {
+                    SkDebugf("WARNING: failed to attach stencil buffer. "
+                             "Rendering may be incorrect.\n");
+                }
             }
         }
         return;
@@ -283,7 +285,7 @@
 // First try to reuse one of the recently allocated/used GrSurfaces in the free pool.
 // If we can't find a useable one, create a new one.
 sk_sp<GrSurface> GrResourceAllocator::findSurfaceFor(const GrSurfaceProxy* proxy,
-                                                     bool needsStencil) {
+                                                     int minStencilSampleCount) {
 
     if (proxy->asTextureProxy() && proxy->asTextureProxy()->getUniqueKey().isValid()) {
         // First try to reattach to a cached version if the proxy is uniquely keyed
@@ -291,7 +293,7 @@
                                                         proxy->asTextureProxy()->getUniqueKey());
         if (surface) {
             if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(fResourceProvider, surface.get(),
-                                                           needsStencil)) {
+                                                           minStencilSampleCount)) {
                 return nullptr;
             }
 
@@ -317,7 +319,7 @@
         }
 
         if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(fResourceProvider, surface.get(),
-                                                       needsStencil)) {
+                                                       minStencilSampleCount)) {
             return nullptr;
         }
         SkASSERT(!surface->getUniqueKey().isValid());
@@ -422,13 +424,13 @@
 
         this->expire(cur->start());
 
-        bool needsStencil = cur->proxy()->asRenderTargetProxy()
-                                            ? cur->proxy()->asRenderTargetProxy()->needsStencil()
-                                            : false;
+        int minStencilSampleCount = (cur->proxy()->asRenderTargetProxy())
+                ? cur->proxy()->asRenderTargetProxy()->numStencilSamples()
+                : 0;
 
         if (cur->proxy()->isInstantiated()) {
             if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(
-                        fResourceProvider, cur->proxy()->peekSurface(), needsStencil)) {
+                        fResourceProvider, cur->proxy()->peekSurface(), minStencilSampleCount)) {
                 *outError = AssignError::kFailedProxyInstantiation;
             }
 
@@ -454,7 +456,8 @@
                     fDeinstantiateTracker->addProxy(cur->proxy());
                 }
             }
-        } else if (sk_sp<GrSurface> surface = this->findSurfaceFor(cur->proxy(), needsStencil)) {
+        } else if (sk_sp<GrSurface> surface = this->findSurfaceFor(
+                cur->proxy(), minStencilSampleCount)) {
             // TODO: make getUniqueKey virtual on GrSurfaceProxy
             GrTextureProxy* texProxy = cur->proxy()->asTextureProxy();
 
diff --git a/src/gpu/GrResourceAllocator.h b/src/gpu/GrResourceAllocator.h
index fae6657..b448c2d 100644
--- a/src/gpu/GrResourceAllocator.h
+++ b/src/gpu/GrResourceAllocator.h
@@ -127,7 +127,7 @@
 
     // These two methods wrap the interactions with the free pool
     void recycleSurface(sk_sp<GrSurface> surface);
-    sk_sp<GrSurface> findSurfaceFor(const GrSurfaceProxy* proxy, bool needsStencil);
+    sk_sp<GrSurface> findSurfaceFor(const GrSurfaceProxy* proxy, int minStencilSampleCount);
 
     struct FreePoolTraits {
         static const GrScratchKey& GetKey(const GrSurface& s) {
diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp
index 332dd25..0ecf1b1 100644
--- a/src/gpu/GrResourceProvider.cpp
+++ b/src/gpu/GrResourceProvider.cpp
@@ -386,9 +386,10 @@
     return buffer;
 }
 
-bool GrResourceProvider::attachStencilAttachment(GrRenderTarget* rt) {
+bool GrResourceProvider::attachStencilAttachment(GrRenderTarget* rt, int minStencilSampleCount) {
     SkASSERT(rt);
-    if (rt->renderTargetPriv().getStencilAttachment()) {
+    GrStencilAttachment* stencil = rt->renderTargetPriv().getStencilAttachment();
+    if (stencil && stencil->numSamples() >= minStencilSampleCount) {
         return true;
     }
 
@@ -403,12 +404,13 @@
             height = SkNextPow2(height);
         }
 #endif
-        GrStencilAttachment::ComputeSharedStencilAttachmentKey(width, height,
-                                                               rt->numSamples(), &sbKey);
+        GrStencilAttachment::ComputeSharedStencilAttachmentKey(
+                width, height, minStencilSampleCount, &sbKey);
         auto stencil = this->findByUniqueKey<GrStencilAttachment>(sbKey);
         if (!stencil) {
             // Need to try and create a new stencil
-            stencil.reset(this->gpu()->createStencilAttachmentForRenderTarget(rt, width, height));
+            stencil.reset(this->gpu()->createStencilAttachmentForRenderTarget(
+                    rt, width, height, minStencilSampleCount));
             if (!stencil) {
                 return false;
             }
diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h
index 00bf3d2..46b4710 100644
--- a/src/gpu/GrResourceProvider.h
+++ b/src/gpu/GrResourceProvider.h
@@ -210,10 +210,10 @@
                                     const void* data = nullptr);
 
     /**
-     * If passed in render target already has a stencil buffer, return true. Otherwise attempt to
-     * attach one and return true on success.
+     * If passed in render target already has a stencil buffer with at least "numSamples" samples,
+     * return true. Otherwise attempt to attach one and return true on success.
      */
-    bool attachStencilAttachment(GrRenderTarget* rt);
+    bool attachStencilAttachment(GrRenderTarget* rt, int numStencilSamples);
 
      /**
       * Wraps an existing texture with a GrRenderTarget object. This is useful when the provided
diff --git a/src/gpu/GrSurfaceProxy.cpp b/src/gpu/GrSurfaceProxy.cpp
index 09224be..6c2ecb3 100644
--- a/src/gpu/GrSurfaceProxy.cpp
+++ b/src/gpu/GrSurfaceProxy.cpp
@@ -17,6 +17,7 @@
 #include "src/gpu/GrOpList.h"
 #include "src/gpu/GrProxyProvider.h"
 #include "src/gpu/GrRecordingContextPriv.h"
+#include "src/gpu/GrStencilAttachment.h"
 #include "src/gpu/GrSurfaceContext.h"
 #include "src/gpu/GrSurfaceContextPriv.h"
 #include "src/gpu/GrSurfacePriv.h"
@@ -116,15 +117,15 @@
 }
 
 bool GrSurfaceProxyPriv::AttachStencilIfNeeded(GrResourceProvider* resourceProvider,
-                                               GrSurface* surface, bool needsStencil) {
-    if (needsStencil) {
+                                               GrSurface* surface, int minStencilSampleCount) {
+    if (minStencilSampleCount) {
         GrRenderTarget* rt = surface->asRenderTarget();
         if (!rt) {
             SkASSERT(0);
             return false;
         }
 
-        if (!resourceProvider->attachStencilAttachment(rt)) {
+        if (!resourceProvider->attachStencilAttachment(rt, minStencilSampleCount)) {
             return false;
         }
     }
@@ -133,7 +134,7 @@
 }
 
 sk_sp<GrSurface> GrSurfaceProxy::createSurfaceImpl(GrResourceProvider* resourceProvider,
-                                                   int sampleCnt, bool needsStencil,
+                                                   int sampleCnt, int minStencilSampleCount,
                                                    GrSurfaceDescFlags descFlags,
                                                    GrMipMapped mipMapped) const {
     SkASSERT(GrSurfaceProxy::LazyState::kNot == this->lazyInstantiationState());
@@ -187,7 +188,7 @@
     }
 
     if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, surface.get(),
-                                                   needsStencil)) {
+                                                   minStencilSampleCount)) {
         return nullptr;
     }
 
@@ -219,8 +220,10 @@
 #ifdef SK_DEBUG
     if (this->asRenderTargetProxy()) {
         SkASSERT(fTarget->asRenderTarget());
-        if (this->asRenderTargetProxy()->needsStencil()) {
-           SkASSERT(fTarget->asRenderTarget()->renderTargetPriv().getStencilAttachment());
+        if (int minStencilSampleCount = this->asRenderTargetProxy()->numStencilSamples()) {
+            auto* stencil = fTarget->asRenderTarget()->renderTargetPriv().getStencilAttachment();
+            SkASSERT(stencil);
+            SkASSERT(stencil->numSamples() >= minStencilSampleCount);
         }
     }
 
@@ -231,7 +234,7 @@
 }
 
 bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt,
-                                     bool needsStencil, GrSurfaceDescFlags descFlags,
+                                     int minStencilSampleCount, GrSurfaceDescFlags descFlags,
                                      GrMipMapped mipMapped, const GrUniqueKey* uniqueKey) {
     SkASSERT(LazyState::kNot == this->lazyInstantiationState());
     if (fTarget) {
@@ -239,11 +242,11 @@
             SkASSERT(fTarget->getUniqueKey().isValid() && fTarget->getUniqueKey() == *uniqueKey);
         }
         return GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, fTarget.get(),
-                                                         needsStencil);
+                                                         minStencilSampleCount);
     }
 
-    sk_sp<GrSurface> surface = this->createSurfaceImpl(resourceProvider, sampleCnt, needsStencil,
-                                                       descFlags, mipMapped);
+    sk_sp<GrSurface> surface = this->createSurfaceImpl(
+            resourceProvider, sampleCnt, minStencilSampleCount, descFlags, mipMapped);
     if (!surface) {
         return false;
     }
@@ -479,11 +482,12 @@
     SkASSERT(fProxy->fWidth <= surface->width());
     SkASSERT(fProxy->fHeight <= surface->height());
 
-    bool needsStencil = fProxy->asRenderTargetProxy()
-                                        ? fProxy->asRenderTargetProxy()->needsStencil()
-                                        : false;
+    int minStencilSampleCount = (fProxy->asRenderTargetProxy())
+            ? fProxy->asRenderTargetProxy()->numSamples()
+            : 0;
 
-    if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, surface.get(), needsStencil)) {
+    if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(
+            resourceProvider, surface.get(), minStencilSampleCount)) {
         return false;
     }
 
diff --git a/src/gpu/GrSurfaceProxy.h b/src/gpu/GrSurfaceProxy.h
index 02cbe24..9dd5f92 100644
--- a/src/gpu/GrSurfaceProxy.h
+++ b/src/gpu/GrSurfaceProxy.h
@@ -358,8 +358,9 @@
     virtual sk_sp<GrSurface> createSurface(GrResourceProvider*) const = 0;
     void assign(sk_sp<GrSurface> surface);
 
-    sk_sp<GrSurface> createSurfaceImpl(GrResourceProvider*, int sampleCnt, bool needsStencil,
-                                       GrSurfaceDescFlags, GrMipMapped) const;
+    sk_sp<GrSurface> createSurfaceImpl(
+            GrResourceProvider*, int sampleCnt, int minStencilSampleCount, GrSurfaceDescFlags,
+            GrMipMapped) const;
 
     // Once the size of a fully-lazy proxy is decided, and before it gets instantiated, the client
     // can use this optional method to specify the proxy's size. (A proxy's size can be less than
@@ -372,8 +373,9 @@
         fHeight = height;
     }
 
-    bool instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt, bool needsStencil,
-                         GrSurfaceDescFlags descFlags, GrMipMapped, const GrUniqueKey*);
+    bool instantiateImpl(
+            GrResourceProvider* resourceProvider, int sampleCnt, int minStencilSampleCount,
+            GrSurfaceDescFlags descFlags, GrMipMapped, const GrUniqueKey*);
 
     // For deferred proxies this will be null until the proxy is instantiated.
     // For wrapped proxies it will point to the wrapped resource.
diff --git a/src/gpu/GrSurfaceProxyPriv.h b/src/gpu/GrSurfaceProxyPriv.h
index 0dfb591..c6e4712 100644
--- a/src/gpu/GrSurfaceProxyPriv.h
+++ b/src/gpu/GrSurfaceProxyPriv.h
@@ -50,7 +50,7 @@
     }
 
     static bool SK_WARN_UNUSED_RESULT AttachStencilIfNeeded(GrResourceProvider*, GrSurface*,
-                                                            bool needsStencil);
+                                                            int minStencilSampleCount);
 
     bool ignoredByResourceAllocator() const { return fProxy->ignoredByResourceAllocator(); }
     void setIgnoredByResourceAllocator() { fProxy->setIgnoredByResourceAllocator(); }
diff --git a/src/gpu/GrTextureRenderTargetProxy.cpp b/src/gpu/GrTextureRenderTargetProxy.cpp
index 1803324..a8710f0 100644
--- a/src/gpu/GrTextureRenderTargetProxy.cpp
+++ b/src/gpu/GrTextureRenderTargetProxy.cpp
@@ -91,8 +91,9 @@
 
     const GrUniqueKey& key = this->getUniqueKey();
 
-    if (!this->instantiateImpl(resourceProvider, this->numSamples(), this->needsStencil(),
-                               kDescFlags, this->mipMapped(), key.isValid() ? &key : nullptr)) {
+    if (!this->instantiateImpl(
+            resourceProvider, this->numSamples(), this->numStencilSamples(), kDescFlags,
+            this->mipMapped(), key.isValid() ? &key : nullptr)) {
         return false;
     }
     if (key.isValid()) {
@@ -109,9 +110,9 @@
                                                     GrResourceProvider* resourceProvider) const {
     static constexpr GrSurfaceDescFlags kDescFlags = kRenderTarget_GrSurfaceFlag;
 
-    sk_sp<GrSurface> surface = this->createSurfaceImpl(resourceProvider, this->numSamples(),
-                                                       this->needsStencil(), kDescFlags,
-                                                       this->mipMapped());
+    sk_sp<GrSurface> surface = this->createSurfaceImpl(
+            resourceProvider, this->numSamples(), this->numStencilSamples(), kDescFlags,
+            this->mipMapped());
     if (!surface) {
         return nullptr;
     }
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index f219f2b..ccf6cb8 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -1812,12 +1812,11 @@
     return true;
 }
 
-GrStencilAttachment* GrGLGpu::createStencilAttachmentForRenderTarget(const GrRenderTarget* rt,
-                                                                     int width, int height) {
+GrStencilAttachment* GrGLGpu::createStencilAttachmentForRenderTarget(
+        const GrRenderTarget* rt, int width, int height, int numStencilSamples) {
     SkASSERT(width >= rt->width());
     SkASSERT(height >= rt->height());
 
-    int samples = rt->numSamples();
     GrGLStencilAttachment::IDDesc sbDesc;
 
     int sIdx = this->getCompatibleStencilIndex(rt->config());
@@ -1836,9 +1835,9 @@
     CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
     // we do this "if" so that we don't call the multisample
     // version on a GL that doesn't have an MSAA extension.
-    if (samples > 1) {
+    if (numStencilSamples > 1) {
         SkAssertResult(renderbuffer_storage_msaa(*fGLContext,
-                                                 samples,
+                                                 numStencilSamples,
                                                  sFmt.fInternalFormat,
                                                  width, height));
     } else {
@@ -1856,7 +1855,7 @@
                                                                sbDesc,
                                                                width,
                                                                height,
-                                                               samples,
+                                                               numStencilSamples,
                                                                format);
     return stencil;
 }
@@ -2839,7 +2838,13 @@
 void GrGLGpu::flushHWAAState(GrRenderTarget* rt, bool useHWAA) {
     // rt is only optional if useHWAA is false.
     SkASSERT(rt || !useHWAA);
-    SkASSERT(!useHWAA || rt->numSamples() > 1);
+#ifdef SK_DEBUG
+    if (useHWAA && rt->numSamples() <= 1) {
+        SkASSERT(this->caps()->mixedSamplesSupport());
+        SkASSERT(0 != static_cast<GrGLRenderTarget*>(rt)->renderFBOID());
+        SkASSERT(rt->renderTargetPriv().getStencilAttachment());
+    }
+#endif
 
     if (this->caps()->multisampleDisableSupport()) {
         if (useHWAA) {
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index db6564e..abbcb2b 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -133,9 +133,8 @@
         fHWBoundRenderTargetUniqueID.makeInvalid();
     }
 
-    GrStencilAttachment* createStencilAttachmentForRenderTarget(const GrRenderTarget* rt,
-                                                                int width,
-                                                                int height) override;
+    GrStencilAttachment* createStencilAttachmentForRenderTarget(
+            const GrRenderTarget* rt, int width, int height, int numStencilSamples) override;
     GrBackendTexture createBackendTexture(int w, int h, const GrBackendFormat&,
                                           GrMipMapped, GrRenderable,
                                           const void* pixels, size_t rowBytes,
diff --git a/src/gpu/mock/GrMockGpu.cpp b/src/gpu/mock/GrMockGpu.cpp
index e8c1899..37466bb 100644
--- a/src/gpu/mock/GrMockGpu.cpp
+++ b/src/gpu/mock/GrMockGpu.cpp
@@ -207,9 +207,9 @@
     return sk_sp<GrGpuBuffer>(new GrMockBuffer(this, sizeInBytes, type, accessPattern));
 }
 
-GrStencilAttachment* GrMockGpu::createStencilAttachmentForRenderTarget(const GrRenderTarget* rt,
-                                                                       int width,
-                                                                       int height) {
+GrStencilAttachment* GrMockGpu::createStencilAttachmentForRenderTarget(
+        const GrRenderTarget* rt, int width, int height, int numStencilSamples) {
+    SkASSERT(numStencilSamples == rt->numSamples());
     static constexpr int kBits = 8;
     fStats.incStencilAttachmentCreates();
     return new GrMockStencilAttachment(this, width, height, kBits, rt->numSamples());
diff --git a/src/gpu/mock/GrMockGpu.h b/src/gpu/mock/GrMockGpu.h
index b26c4e1..0ea5c1a 100644
--- a/src/gpu/mock/GrMockGpu.h
+++ b/src/gpu/mock/GrMockGpu.h
@@ -120,9 +120,8 @@
         }
     }
 
-    GrStencilAttachment* createStencilAttachmentForRenderTarget(const GrRenderTarget*,
-                                                                int width,
-                                                                int height) override;
+    GrStencilAttachment* createStencilAttachmentForRenderTarget(
+            const GrRenderTarget*, int width, int height, int numStencilSamples) override;
     GrBackendTexture createBackendTexture(int w, int h, const GrBackendFormat&,
                                           GrMipMapped, GrRenderable,
                                           const void* pixels, size_t rowBytes,
diff --git a/src/gpu/mtl/GrMtlGpu.h b/src/gpu/mtl/GrMtlGpu.h
index 8cd8288..cd26919 100644
--- a/src/gpu/mtl/GrMtlGpu.h
+++ b/src/gpu/mtl/GrMtlGpu.h
@@ -196,9 +196,8 @@
     // Function that fills texture with transparent black
     bool clearTexture(GrMtlTexture*, GrColorType);
 
-    GrStencilAttachment* createStencilAttachmentForRenderTarget(const GrRenderTarget*,
-                                                                int width,
-                                                                int height) override;
+    GrStencilAttachment* createStencilAttachmentForRenderTarget(
+            const GrRenderTarget*, int width, int height, int numStencilSamples) override;
 
     bool createTestingOnlyMtlTextureInfo(GrPixelConfig, MTLPixelFormat,
                                          int w, int h, bool texturable,
diff --git a/src/gpu/mtl/GrMtlGpu.mm b/src/gpu/mtl/GrMtlGpu.mm
index c56e1fc..5e0acde 100644
--- a/src/gpu/mtl/GrMtlGpu.mm
+++ b/src/gpu/mtl/GrMtlGpu.mm
@@ -396,9 +396,9 @@
     return true;
 }
 
-GrStencilAttachment* GrMtlGpu::createStencilAttachmentForRenderTarget(const GrRenderTarget* rt,
-                                                                      int width,
-                                                                      int height) {
+GrStencilAttachment* GrMtlGpu::createStencilAttachmentForRenderTarget(
+        const GrRenderTarget* rt, int width, int height, int numStencilSamples) {
+    SkASSERT(numStencilSamples == rt->numSamples());
     SkASSERT(width >= rt->width());
     SkASSERT(height >= rt->height());
 
diff --git a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
index 58f1037..095064c 100644
--- a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
+++ b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
@@ -38,14 +38,13 @@
     // GrPath doesn't support hairline paths. An arbitrary path effect could produce a hairline
     // path.
     if (args.fShape->style().strokeRec().isHairlineStyle() ||
-        args.fShape->style().hasNonDashPathEffect()) {
+        args.fShape->style().hasNonDashPathEffect() ||
+        args.fHasUserStencilSettings) {
         return CanDrawPath::kNo;
     }
-    if (args.fHasUserStencilSettings) {
-        return CanDrawPath::kNo;
-    }
-    // We rely on a mixed sampled stencil buffer to implement coverage AA.
-    if (GrAAType::kCoverage == args.fAAType) {  // MIXED SAMPLES TODO: "&& !mixedSamplesSupport"
+    if (GrAAType::kCoverage == args.fAAType &&
+        !args.fProxy->canUseMixedSamples(*args.fCaps)) {
+        // We rely on a mixed sampled stencil buffer to implement coverage AA.
         return CanDrawPath::kNo;
     }
     return CanDrawPath::kYes;
@@ -95,10 +94,6 @@
 
     sk_sp<GrPath> path(get_gr_path(fResourceProvider, *args.fShape));
 
-    if (GrAAType::kCoverage == args.fAAType) {
-        // MIXED SAMPLES TODO: Indicate that we need a mixed sampled stencil buffer.
-    }
-
     if (args.fShape->inverseFilled()) {
         SkMatrix vmi;
         if (!viewMatrix.invert(&vmi)) {
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 97e26fc..3b6d2c8 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -1439,9 +1439,9 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-GrStencilAttachment* GrVkGpu::createStencilAttachmentForRenderTarget(const GrRenderTarget* rt,
-                                                                     int width,
-                                                                     int height) {
+GrStencilAttachment* GrVkGpu::createStencilAttachmentForRenderTarget(
+        const GrRenderTarget* rt, int width, int height, int numStencilSamples) {
+    SkASSERT(numStencilSamples == rt->numSamples());
     SkASSERT(width >= rt->width());
     SkASSERT(height >= rt->height());
 
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 8392c48..e86323e 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -99,9 +99,8 @@
     }
 #endif
 
-    GrStencilAttachment* createStencilAttachmentForRenderTarget(const GrRenderTarget*,
-                                                                int width,
-                                                                int height) override;
+    GrStencilAttachment* createStencilAttachmentForRenderTarget(
+            const GrRenderTarget*, int width, int height, int numStencilSamples) override;
 
     GrGpuRTCommandBuffer* getCommandBuffer(
             GrRenderTarget*, GrSurfaceOrigin, const SkRect&,