Don't attempt to use stencil on wrapped, stencil-less targets

We can't attach stencil to a wrapped render target. If we find
ourselves rendering to one that doesn't have stencil already, don't
issue any ops that use stencil.

Bug: chromium:1196353
Bug: skia:11943
Change-Id: I9db93f31a4f8556103be72eb708755e4eaf78136
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/399839
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/GrClipStack.cpp b/src/gpu/GrClipStack.cpp
index 5461bcd..4058395 100644
--- a/src/gpu/GrClipStack.cpp
+++ b/src/gpu/GrClipStack.cpp
@@ -1352,8 +1352,8 @@
         // Disable analytic clips when there are user stencil settings to ensure the clip is
         // respected in the stencil buffer.
         remainingAnalyticFPs = 0;
-        // If we have user stencil settings, we shouldn't be avoiding the stencil buffer anyways.
-        SkASSERT(!context->priv().caps()->avoidStencilBuffers());
+        // If we have user stencil settings, stencil needs to be supported.
+        SkASSERT(rtc->asRenderTargetProxy()->canUseStencil(*context->priv().caps()));
     }
 
     // If window rectangles are supported, we can use them to exclude inner bounds of difference ops
@@ -1469,8 +1469,8 @@
     // Now rasterize any remaining elements, either to the stencil or a SW mask. All elements are
     // flattened into a single mask.
     if (!elementsForMask.empty()) {
-        bool stencilUnavailable = context->priv().caps()->avoidStencilBuffers() ||
-                                  rtc->wrapsVkSecondaryCB();
+        bool stencilUnavailable =
+                !rtc->asRenderTargetProxy()->canUseStencil(*context->priv().caps());
 
         bool hasSWMask = false;
         if ((rtc->numSamples() <= 1 && maskRequiresAA) || stencilUnavailable) {
diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp
index 75f0daa..b7de846 100644
--- a/src/gpu/GrClipStackClip.cpp
+++ b/src/gpu/GrClipStackClip.cpp
@@ -127,8 +127,6 @@
         canDrawArgs.fPaint = nullptr;
         canDrawArgs.fSurfaceProps = &surfaceDrawContext->surfaceProps();
         canDrawArgs.fAAType = aaType;
-        SkASSERT(!surfaceDrawContext->wrapsVkSecondaryCB());
-        canDrawArgs.fTargetIsWrappedVkSecondaryCB = false;
         canDrawArgs.fHasUserStencilSettings = hasUserStencilSettings;
 
         // the 'false' parameter disallows use of the SW path renderer
@@ -155,10 +153,8 @@
     // a clip gets complex enough it can just be done in SW regardless
     // of whether it would invoke the GrSoftwarePathRenderer.
 
-    // If we're avoiding stencils, always use SW. This includes drawing into a wrapped vulkan
-    // secondary command buffer which can't handle stencils.
-    if (context->priv().caps()->avoidStencilBuffers() ||
-        surfaceDrawContext->wrapsVkSecondaryCB()) {
+    // If stencil isn't supported, always use SW.
+    if (!surfaceDrawContext->asRenderTargetProxy()->canUseStencil(*context->priv().caps())) {
         return true;
     }
 
@@ -214,8 +210,8 @@
     if (surfaceDrawContext->numSamples() > 1 || aa == GrAAType::kMSAA || hasUserStencilSettings) {
         // Disable analytic clips when we have MSAA. In MSAA we never conflate coverage and opacity.
         maxAnalyticElements = 0;
-        // We disable MSAA when avoiding stencil.
-        SkASSERT(!context->priv().caps()->avoidStencilBuffers());
+        // We disable MSAA when stencil isn't supported.
+        SkASSERT(surfaceDrawContext->asRenderTargetProxy()->canUseStencil(*context->priv().caps()));
     }
     auto* ccpr = context->priv().drawingManager()->getCoverageCountingPathRenderer();
 
@@ -274,8 +270,7 @@
 #endif
 
     if ((surfaceDrawContext->numSamples() <= 1 && reducedClip.maskRequiresAA()) ||
-        context->priv().caps()->avoidStencilBuffers() ||
-        surfaceDrawContext->wrapsVkSecondaryCB()) {
+        !surfaceDrawContext->asRenderTargetProxy()->canUseStencil(*context->priv().caps())) {
         GrSurfaceProxyView result;
         if (UseSWOnlyPath(context, hasUserStencilSettings, surfaceDrawContext, reducedClip)) {
             // The clip geometry is complex enough that it will be more efficient to create it
@@ -295,8 +290,7 @@
 
         // If alpha or software clip mask creation fails, fall through to the stencil code paths,
         // unless stencils are disallowed.
-        if (context->priv().caps()->avoidStencilBuffers() ||
-            surfaceDrawContext->wrapsVkSecondaryCB()) {
+        if (!surfaceDrawContext->asRenderTargetProxy()->canUseStencil(*context->priv().caps())) {
             SkDebugf("WARNING: Clip mask requires stencil, but stencil unavailable. "
                      "Clip will be ignored.\n");
             return false;
diff --git a/src/gpu/GrOpsTask.cpp b/src/gpu/GrOpsTask.cpp
index 862f151..20f97cc 100644
--- a/src/gpu/GrOpsTask.cpp
+++ b/src/gpu/GrOpsTask.cpp
@@ -574,6 +574,7 @@
 
     GrAttachment* stencil = nullptr;
     if (proxy->needsStencil()) {
+        SkASSERT(proxy->canUseStencil(caps));
         if (!flushState->resourceProvider()->attachStencilAttachment(renderTarget,
                                                                      fUsesMSAASurface)) {
             SkDebugf("WARNING: failed to attach a stencil buffer. Rendering will be skipped.\n");
diff --git a/src/gpu/GrPathRenderer.cpp b/src/gpu/GrPathRenderer.cpp
index f06089c..eaee4e2 100644
--- a/src/gpu/GrPathRenderer.cpp
+++ b/src/gpu/GrPathRenderer.cpp
@@ -53,7 +53,6 @@
     canArgs.fPaint = &args.fPaint;
     canArgs.fSurfaceProps = &args.fRenderTargetContext->surfaceProps();
     canArgs.fAAType = args.fAAType;
-    canArgs.fTargetIsWrappedVkSecondaryCB = args.fRenderTargetContext->wrapsVkSecondaryCB();
     canArgs.validate();
 
     canArgs.fHasUserStencilSettings = !args.fUserStencilSettings->isUnused();
diff --git a/src/gpu/GrPathRenderer.h b/src/gpu/GrPathRenderer.h
index 248950e..873d8fc 100644
--- a/src/gpu/GrPathRenderer.h
+++ b/src/gpu/GrPathRenderer.h
@@ -86,7 +86,6 @@
         const GrPaint*              fPaint;
         const SkSurfaceProps*       fSurfaceProps;
         GrAAType                    fAAType;
-        bool                        fTargetIsWrappedVkSecondaryCB;
 
         // This is only used by GrTessellationPathRenderer
         bool                        fHasUserStencilSettings;
diff --git a/src/gpu/GrRenderTargetProxy.cpp b/src/gpu/GrRenderTargetProxy.cpp
index fdbffad..48c395c 100644
--- a/src/gpu/GrRenderTargetProxy.cpp
+++ b/src/gpu/GrRenderTargetProxy.cpp
@@ -89,6 +89,33 @@
     return true;
 }
 
+bool GrRenderTargetProxy::canUseStencil(const GrCaps& caps) const {
+    if (caps.avoidStencilBuffers() || this->wrapsVkSecondaryCB()) {
+        return false;
+    }
+    if (!this->isInstantiated()) {
+        if (this->isLazy() && this->backendFormat().backend() == GrBackendApi::kOpenGL) {
+            // It's possible for wrapped GL render targets to not have stencil. We don't currently
+            // have an exact way of knowing whether the target will be able to use stencil, so we do
+            // the best we can: if a lazy GL proxy doesn't have a texture, then it might be a
+            // wrapped target without stencil, so we conservatively block stencil.
+            // FIXME: skbug.com/11943: SkSurfaceCharacterization needs a "canUseStencil" flag.
+            return SkToBool(this->asTextureProxy());
+        } else {
+            // Otherwise the target will definitely not be wrapped. Ganesh is free to attach
+            // stencils on internal render targets.
+            return true;
+        }
+    }
+    // Just ask the actual target if we can use stencil.
+    GrRenderTarget* rt = this->peekRenderTarget();
+    // The dmsaa attachment (if any) always supports stencil. The real question is whether the
+    // non-dmsaa attachment supports stencil.
+    bool useMSAASurface = rt->numSamples() > 1;
+    return rt->getStencilAttachment(useMSAASurface) ||
+           rt->canAttemptStencilAttachment(useMSAASurface);
+}
+
 sk_sp<GrSurface> GrRenderTargetProxy::createSurface(GrResourceProvider* resourceProvider) const {
     sk_sp<GrSurface> surface = this->createSurfaceImpl(resourceProvider, fSampleCnt,
                                                        GrRenderable::kYes, GrMipmapped::kNo);
diff --git a/src/gpu/GrRenderTargetProxy.h b/src/gpu/GrRenderTargetProxy.h
index f869262..debeaea 100644
--- a/src/gpu/GrRenderTargetProxy.h
+++ b/src/gpu/GrRenderTargetProxy.h
@@ -54,6 +54,11 @@
     // Actually instantiate the backing rendertarget, if necessary.
     bool instantiate(GrResourceProvider*) override;
 
+    // Returns true if this proxy either has a stencil attachment already, or if we can attach one
+    // during flush. Wrapped render targets without stencil will return false, since we are unable
+    // to modify their attachments.
+    bool canUseStencil(const GrCaps& caps) const;
+
     /*
      * Indicate that a draw to this proxy requires stencil.
      */
diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp
index f0e6f1c..92b5fca 100644
--- a/src/gpu/GrResourceProvider.cpp
+++ b/src/gpu/GrResourceProvider.cpp
@@ -504,6 +504,8 @@
 
 bool GrResourceProvider::attachStencilAttachment(GrRenderTarget* rt, bool useMSAASurface) {
     SkASSERT(rt);
+    SkASSERT(!this->caps()->avoidStencilBuffers());
+
     GrAttachment* stencil = rt->getStencilAttachment(useMSAASurface);
     if (stencil) {
         SkASSERT(stencil->numSamples() == num_stencil_samples(rt, useMSAASurface, *this->caps()));
diff --git a/src/gpu/GrStencilMaskHelper.cpp b/src/gpu/GrStencilMaskHelper.cpp
index 67e24ea..ecebf19 100644
--- a/src/gpu/GrStencilMaskHelper.cpp
+++ b/src/gpu/GrStencilMaskHelper.cpp
@@ -420,7 +420,6 @@
     canDrawArgs.fSurfaceProps = &fRTC->surfaceProps();
     canDrawArgs.fAAType = pathAAType;
     canDrawArgs.fHasUserStencilSettings = false;
-    canDrawArgs.fTargetIsWrappedVkSecondaryCB = fRTC->wrapsVkSecondaryCB();
 
     GrPathRenderer* pr =  fContext->priv().drawingManager()->getPathRenderer(
             canDrawArgs, false, GrPathRendererChain::DrawType::kStencil, &stencilSupport);
diff --git a/src/gpu/GrSurfaceDrawContext.cpp b/src/gpu/GrSurfaceDrawContext.cpp
index 4d97d85..323e5e6 100644
--- a/src/gpu/GrSurfaceDrawContext.cpp
+++ b/src/gpu/GrSurfaceDrawContext.cpp
@@ -928,7 +928,6 @@
     canDrawArgs.fSurfaceProps = &fSurfaceProps;
     canDrawArgs.fAAType = (doStencilMSAA == GrAA::kYes) ? GrAAType::kMSAA : GrAAType::kNone;
     canDrawArgs.fHasUserStencilSettings = false;
-    canDrawArgs.fTargetIsWrappedVkSecondaryCB = this->wrapsVkSecondaryCB();
     GrPathRenderer* pr = this->drawingManager()->getPathRenderer(
             canDrawArgs, false, GrPathRendererChain::DrawType::kStencil);
     if (!pr) {
@@ -1621,8 +1620,6 @@
     canDrawArgs.fSurfaceProps = &fSurfaceProps;
     canDrawArgs.fClipConservativeBounds = &clipConservativeBounds;
     canDrawArgs.fAAType = aaType;
-    SkASSERT(!this->wrapsVkSecondaryCB());
-    canDrawArgs.fTargetIsWrappedVkSecondaryCB = false;
     canDrawArgs.fHasUserStencilSettings = hasUserStencilSettings;
 
     // Don't allow the SW renderer
@@ -1785,7 +1782,6 @@
     canDrawArgs.fPaint = &paint;
     canDrawArgs.fSurfaceProps = &fSurfaceProps;
     canDrawArgs.fClipConservativeBounds = &clipConservativeBounds;
-    canDrawArgs.fTargetIsWrappedVkSecondaryCB = this->wrapsVkSecondaryCB();
     canDrawArgs.fHasUserStencilSettings = false;
     canDrawArgs.fAAType = aaType;
 
diff --git a/src/gpu/gl/GrGLRenderTarget.cpp b/src/gpu/gl/GrGLRenderTarget.cpp
index 9151101..98657b4 100644
--- a/src/gpu/gl/GrGLRenderTarget.cpp
+++ b/src/gpu/gl/GrGLRenderTarget.cpp
@@ -252,10 +252,8 @@
 }
 
 bool GrGLRenderTarget::canAttemptStencilAttachment(bool useMultisampleFBO) const {
-    if (this->getGpu()->getContext()->priv().caps()->avoidStencilBuffers()) {
-        return false;
-    }
-
+    // This cap should have been handled at a higher level.
+    SkASSERT(!this->getGpu()->getContext()->priv().caps()->avoidStencilBuffers());
     // Only modify the FBO's attachments if we have created the FBO. Public APIs do not currently
     // allow for borrowed FBO ownership, so we can safely assume that if an object is owned,
     // Skia created it.
diff --git a/src/gpu/gl/GrGLTextureRenderTarget.cpp b/src/gpu/gl/GrGLTextureRenderTarget.cpp
index 75d642c..e3e8abf 100644
--- a/src/gpu/gl/GrGLTextureRenderTarget.cpp
+++ b/src/gpu/gl/GrGLTextureRenderTarget.cpp
@@ -54,9 +54,10 @@
 }
 
 bool GrGLTextureRenderTarget::canAttemptStencilAttachment(bool useMultisampleFBO) const {
-    // The RT FBO of GrGLTextureRenderTarget is never created from a
-    // wrapped FBO, so we only care about the flag.
-    return !this->getGpu()->getContext()->priv().caps()->avoidStencilBuffers();
+    // This cap should have been handled at a higher level.
+    SkASSERT(!this->getGpu()->getContext()->priv().caps()->avoidStencilBuffers());
+    // The RT FBO of GrGLTextureRenderTarget is never created from a wrapped FBO.
+    return true;
 }
 
 sk_sp<GrGLTextureRenderTarget> GrGLTextureRenderTarget::MakeWrapped(
diff --git a/src/gpu/ops/GrDefaultPathRenderer.cpp b/src/gpu/ops/GrDefaultPathRenderer.cpp
index 5ca9e70..9d19886 100644
--- a/src/gpu/ops/GrDefaultPathRenderer.cpp
+++ b/src/gpu/ops/GrDefaultPathRenderer.cpp
@@ -700,7 +700,7 @@
             args.fShape->style(), *args.fViewMatrix, nullptr);
     // If we aren't a single_pass_shape or hairline, we require stencil buffers.
     if (!(single_pass_shape(*args.fShape) || isHairline) &&
-        (args.fCaps->avoidStencilBuffers() || args.fTargetIsWrappedVkSecondaryCB)) {
+        !args.fProxy->canUseStencil(*args.fCaps)) {
         return CanDrawPath::kNo;
     }
     // If antialiasing is required, we only support MSAA.
diff --git a/src/gpu/tessellate/GrTessellationPathRenderer.cpp b/src/gpu/tessellate/GrTessellationPathRenderer.cpp
index 5eadbab..8804a4a 100644
--- a/src/gpu/tessellate/GrTessellationPathRenderer.cpp
+++ b/src/gpu/tessellate/GrTessellationPathRenderer.cpp
@@ -134,7 +134,7 @@
         shape.style().strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style ||
         shape.inverseFilled() ||
         args.fHasUserStencilSettings ||
-        args.fTargetIsWrappedVkSecondaryCB) {
+        !args.fProxy->canUseStencil(*args.fCaps)) {
         return CanDrawPath::kNo;
     }
     // On platforms that don't have native support for indirect draws and/or hardware tessellation,