Avoid GrEllipseEffect for small radii on devices without 32 bit float.

Also limit small radius bail in GrCircleEffect to clip out cases.

Change-Id: I14ce736969b05203219d68f30283c36c84f78f3a
Reviewed-on: https://skia-review.googlesource.com/80621
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/gm/bigrrectaaeffect.cpp b/gm/bigrrectaaeffect.cpp
index 62c9da8..a5d77fc 100644
--- a/gm/bigrrectaaeffect.cpp
+++ b/gm/bigrrectaaeffect.cpp
@@ -77,7 +77,8 @@
 
                 SkRRect rrect = fRRect;
                 rrect.offset(SkIntToScalar(x + kGap), SkIntToScalar(y + kGap));
-                std::unique_ptr<GrFragmentProcessor> fp(GrRRectEffect::Make(edgeType, rrect));
+                const auto& caps = *renderTargetContext->caps()->shaderCaps();
+                auto fp = GrRRectEffect::Make(edgeType, rrect, caps);
                 SkASSERT(fp);
                 if (fp) {
                     GrPaint grPaint;
diff --git a/gm/rrects.cpp b/gm/rrects.cpp
index 217e528..dfeebf3 100644
--- a/gm/rrects.cpp
+++ b/gm/rrects.cpp
@@ -106,7 +106,8 @@
                         SkRRect rrect = fRRects[curRRect];
                         rrect.offset(SkIntToScalar(x), SkIntToScalar(y));
                         GrClipEdgeType edgeType = (GrClipEdgeType) et;
-                        auto fp = GrRRectEffect::Make(edgeType, rrect);
+                        const auto& caps = *renderTargetContext->caps()->shaderCaps();
+                        auto fp = GrRRectEffect::Make(edgeType, rrect, caps);
                         if (fp) {
                             GrPaint grPaint;
                             grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
diff --git a/gm/windowrectangles.cpp b/gm/windowrectangles.cpp
index 6a3f2a1..a2c8f0b 100644
--- a/gm/windowrectangles.cpp
+++ b/gm/windowrectangles.cpp
@@ -181,7 +181,8 @@
         return;
     }
 
-    const GrReducedClip reducedClip(stack, SkRect::Make(kCoverRect), kNumWindows);
+    const GrReducedClip reducedClip(stack, SkRect::Make(kCoverRect), rtc->caps()->shaderCaps(),
+                                    kNumWindows);
 
     GrPaint paint;
     if (GrFSAAType::kNone == rtc->fsaaType()) {
diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp
index 684a52d..bd6ed9a 100644
--- a/src/gpu/GrClipStackClip.cpp
+++ b/src/gpu/GrClipStackClip.cpp
@@ -201,8 +201,9 @@
         SkASSERT(!context->caps()->avoidStencilBuffers()); // We disable MSAA when avoiding stencil.
     }
 
-    GrReducedClip reducedClip(*fStack, devBounds, renderTargetContext->priv().maxWindowRectangles(),
-                              maxAnalyticFPs);
+    const auto* caps = context->caps()->shaderCaps();
+    GrReducedClip reducedClip(*fStack, devBounds, caps,
+                              renderTargetContext->priv().maxWindowRectangles(), maxAnalyticFPs);
 
     if (reducedClip.hasScissor() && !GrClip::IsInsideClip(reducedClip.scissor(), devBounds)) {
         out->hardClip().addScissor(reducedClip.scissor(), bounds);
diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp
index 883ed5d..f481a10 100644
--- a/src/gpu/GrReducedClip.cpp
+++ b/src/gpu/GrReducedClip.cpp
@@ -32,9 +32,8 @@
  * take a rect in case the caller knows a bound on what is to be drawn through this clip.
  */
 GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds,
-                             int maxWindowRectangles, int maxAnalyticFPs)
-        : fMaxWindowRectangles(maxWindowRectangles)
-        , fMaxAnalyticFPs(maxAnalyticFPs) {
+                             const GrShaderCaps* caps, int maxWindowRectangles, int maxAnalyticFPs)
+        : fCaps(caps), fMaxWindowRectangles(maxWindowRectangles), fMaxAnalyticFPs(maxAnalyticFPs) {
     SkASSERT(!queryBounds.isEmpty());
     SkASSERT(fMaxWindowRectangles <= GrWindowRectangles::kMaxWindows);
     fHasScissor = false;
@@ -588,7 +587,7 @@
                                       : GrClipEdgeType::kInverseFillBW;
     }
 
-    if (auto fp = make_analytic_clip_fp(edgeType, deviceSpaceShape)) {
+    if (auto fp = make_analytic_clip_fp(edgeType, deviceSpaceShape, *fCaps)) {
         fAnalyticFPs.push_back(std::move(fp));
         return ClipResult::kClipped;
     }
@@ -597,17 +596,20 @@
 }
 
 std::unique_ptr<GrFragmentProcessor> make_analytic_clip_fp(GrClipEdgeType edgeType,
-                                                           const SkRect& deviceSpaceRect) {
+                                                           const SkRect& deviceSpaceRect,
+                                                           const GrShaderCaps&) {
     return GrConvexPolyEffect::Make(edgeType, deviceSpaceRect);
 }
 
 std::unique_ptr<GrFragmentProcessor> make_analytic_clip_fp(GrClipEdgeType edgeType,
-                                                           const SkRRect& deviceSpaceRRect) {
-    return GrRRectEffect::Make(edgeType, deviceSpaceRRect);
+                                                           const SkRRect& deviceSpaceRRect,
+                                                           const GrShaderCaps& caps) {
+    return GrRRectEffect::Make(edgeType, deviceSpaceRRect, caps);
 }
 
 std::unique_ptr<GrFragmentProcessor> make_analytic_clip_fp(GrClipEdgeType edgeType,
-                                                           const SkPath& deviceSpacePath) {
+                                                           const SkPath& deviceSpacePath,
+                                                           const GrShaderCaps&) {
     return GrConvexPolyEffect::Make(edgeType, deviceSpacePath);
 }
 
diff --git a/src/gpu/GrReducedClip.h b/src/gpu/GrReducedClip.h
index 22387d7..0922d91 100644
--- a/src/gpu/GrReducedClip.h
+++ b/src/gpu/GrReducedClip.h
@@ -25,7 +25,7 @@
     using Element = SkClipStack::Element;
     using ElementList = SkTLList<SkClipStack::Element, 16>;
 
-    GrReducedClip(const SkClipStack&, const SkRect& queryBounds,
+    GrReducedClip(const SkClipStack&, const SkRect& queryBounds, const GrShaderCaps* caps,
                   int maxWindowRectangles = 0, int maxAnalyticFPs = 0);
 
     /**
@@ -117,6 +117,7 @@
 
     void makeEmpty();
 
+    const GrShaderCaps* fCaps;
     const int fMaxWindowRectangles;
     const int fMaxAnalyticFPs;
     SkIRect fScissor;
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index c9cc395..1f7f645 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -1276,13 +1276,14 @@
         inverseVM.reset();
     }
 
+    const auto& caps = *this->caps()->shaderCaps();
     // TODO these need to be a geometry processors
-    std::unique_ptr<GrFragmentProcessor> innerEffect(GrRRectEffect::Make(innerEdgeType, *inner));
+    auto innerEffect = GrRRectEffect::Make(innerEdgeType, *inner, caps);
     if (!innerEffect) {
         return false;
     }
 
-    std::unique_ptr<GrFragmentProcessor> outerEffect(GrRRectEffect::Make(outerEdgeType, *outer));
+    auto outerEffect = GrRRectEffect::Make(outerEdgeType, *outer, caps);
     if (!outerEffect) {
         return false;
     }
diff --git a/src/gpu/effects/GrCircleEffect.fp b/src/gpu/effects/GrCircleEffect.fp
index 352e824..0faf7d7 100644
--- a/src/gpu/effects/GrCircleEffect.fp
+++ b/src/gpu/effects/GrCircleEffect.fp
@@ -20,7 +20,7 @@
                                                      float radius) {
         // A radius below half causes the implicit insetting done by this processor to become
         // inverted. We could handle this case by making the processor code more complicated.
-        if (radius < .5f) {
+        if (radius < .5f && GrProcessorEdgeTypeIsInverseFill(edgeType)) {
             return nullptr;
         }
         return std::unique_ptr<GrFragmentProcessor>(new GrCircleEffect(edgeType, center, radius));
diff --git a/src/gpu/effects/GrCircleEffect.h b/src/gpu/effects/GrCircleEffect.h
index d721a78..cfc5aaf 100644
--- a/src/gpu/effects/GrCircleEffect.h
+++ b/src/gpu/effects/GrCircleEffect.h
@@ -24,7 +24,7 @@
                                                      float radius) {
         // A radius below half causes the implicit insetting done by this processor to become
         // inverted. We could handle this case by making the processor code more complicated.
-        if (radius < .5f) {
+        if (radius < .5f && GrProcessorEdgeTypeIsInverseFill(edgeType)) {
             return nullptr;
         }
         return std::unique_ptr<GrFragmentProcessor>(new GrCircleEffect(edgeType, center, radius));
diff --git a/src/gpu/effects/GrEllipseEffect.cpp b/src/gpu/effects/GrEllipseEffect.cpp
index b41af39..88a07f9 100644
--- a/src/gpu/effects/GrEllipseEffect.cpp
+++ b/src/gpu/effects/GrEllipseEffect.cpp
@@ -139,7 +139,8 @@
     do {
         et = (GrClipEdgeType)testData->fRandom->nextULessThan(kGrClipEdgeTypeCnt);
     } while (GrClipEdgeType::kHairlineAA == et);
-    return GrEllipseEffect::Make(et, center, SkPoint::Make(rx, ry));
+    return GrEllipseEffect::Make(et, center, SkPoint::Make(rx, ry),
+                                 *testData->caps()->shaderCaps());
 }
 #endif
 #endif
diff --git a/src/gpu/effects/GrEllipseEffect.fp b/src/gpu/effects/GrEllipseEffect.fp
index 27beb85..24cbec2 100644
--- a/src/gpu/effects/GrEllipseEffect.fp
+++ b/src/gpu/effects/GrEllipseEffect.fp
@@ -5,6 +5,10 @@
  * found in the LICENSE file.
  */
 
+@header {
+    #include "GrShaderCaps.h"
+}
+
 layout(key) in GrClipEdgeType edgeType;
 in float2 center;
 in float2 radii;
@@ -18,6 +22,17 @@
 bool useScale = !sk_Caps.floatIs32Bits;
 layout(when=useScale) uniform float2 scale;
 
+@make {
+    static std::unique_ptr<GrFragmentProcessor> Make(GrClipEdgeType edgeType, SkPoint center,
+                                                     SkPoint radii, const GrShaderCaps& caps) {
+        // Small radii produce bad results on devices without full float.
+        if (!caps.floatIs32Bits() && (radii.fX < 0.5f || radii.fY < 0.5f)) {
+            return nullptr;
+        }
+        return std::unique_ptr<GrFragmentProcessor>(new GrEllipseEffect(edgeType, center, radii));
+    }
+}
+
 @optimizationFlags { kCompatibleWithCoverageAsAlpha_OptimizationFlag }
 
 @setData(pdman) {
@@ -101,5 +116,6 @@
     do {
         et = (GrClipEdgeType) testData->fRandom->nextULessThan(kGrClipEdgeTypeCnt);
     } while (GrClipEdgeType::kHairlineAA == et);
-    return GrEllipseEffect::Make(et, center, SkPoint::Make(rx, ry));
+    return GrEllipseEffect::Make(et, center, SkPoint::Make(rx, ry),
+                                 *testData->caps()->shaderCaps());
 }
diff --git a/src/gpu/effects/GrEllipseEffect.h b/src/gpu/effects/GrEllipseEffect.h
index 8769295..b892f98 100644
--- a/src/gpu/effects/GrEllipseEffect.h
+++ b/src/gpu/effects/GrEllipseEffect.h
@@ -12,6 +12,8 @@
 #define GrEllipseEffect_DEFINED
 #include "SkTypes.h"
 #if SK_SUPPORT_GPU
+
+#include "GrShaderCaps.h"
 #include "GrFragmentProcessor.h"
 #include "GrCoordTransform.h"
 class GrEllipseEffect : public GrFragmentProcessor {
@@ -19,8 +21,13 @@
     GrClipEdgeType edgeType() const { return fEdgeType; }
     SkPoint center() const { return fCenter; }
     SkPoint radii() const { return fRadii; }
+
     static std::unique_ptr<GrFragmentProcessor> Make(GrClipEdgeType edgeType, SkPoint center,
-                                                     SkPoint radii) {
+                                                     SkPoint radii, const GrShaderCaps& caps) {
+        // Small radii produce bad results on devices without full float.
+        if (!caps.floatIs32Bits() && (radii.fX < 0.5f || radii.fY < 0.5f)) {
+            return nullptr;
+        }
         return std::unique_ptr<GrFragmentProcessor>(new GrEllipseEffect(edgeType, center, radii));
     }
     GrEllipseEffect(const GrEllipseEffect& src);
diff --git a/src/gpu/effects/GrOvalEffect.cpp b/src/gpu/effects/GrOvalEffect.cpp
index 0c0f3bd..a475ec0 100644
--- a/src/gpu/effects/GrOvalEffect.cpp
+++ b/src/gpu/effects/GrOvalEffect.cpp
@@ -11,8 +11,8 @@
 #include "GrEllipseEffect.h"
 #include "SkRect.h"
 
-std::unique_ptr<GrFragmentProcessor> GrOvalEffect::Make(GrClipEdgeType edgeType,
-                                                        const SkRect& oval) {
+std::unique_ptr<GrFragmentProcessor> GrOvalEffect::Make(GrClipEdgeType edgeType, const SkRect& oval,
+                                                        const GrShaderCaps& caps) {
     if (GrClipEdgeType::kHairlineAA == edgeType) {
         return nullptr;
     }
@@ -26,7 +26,7 @@
         w /= 2;
         h /= 2;
         return GrEllipseEffect::Make(edgeType, SkPoint::Make(oval.fLeft + w, oval.fTop + h),
-                                     SkPoint::Make(w, h));
+                                     SkPoint::Make(w, h), caps);
     }
 
     return nullptr;
diff --git a/src/gpu/effects/GrOvalEffect.h b/src/gpu/effects/GrOvalEffect.h
index f85df89..c102a0b 100644
--- a/src/gpu/effects/GrOvalEffect.h
+++ b/src/gpu/effects/GrOvalEffect.h
@@ -13,6 +13,7 @@
 #include "SkRefCnt.h"
 
 class GrFragmentProcessor;
+class GrShaderCaps;
 struct SkRect;
 
 namespace GrOvalEffect {
@@ -20,8 +21,7 @@
 /**
  * Creates an effect that performs clipping against an oval.
  */
-std::unique_ptr<GrFragmentProcessor> Make(GrClipEdgeType, const SkRect&);
-
+std::unique_ptr<GrFragmentProcessor> Make(GrClipEdgeType, const SkRect&, const GrShaderCaps&);
 };
 
 #endif
diff --git a/src/gpu/effects/GrRRectEffect.cpp b/src/gpu/effects/GrRRectEffect.cpp
index 510a26b..871578f 100644
--- a/src/gpu/effects/GrRRectEffect.cpp
+++ b/src/gpu/effects/GrRRectEffect.cpp
@@ -122,7 +122,7 @@
     do {
         GrClipEdgeType et =
                 (GrClipEdgeType)d->fRandom->nextULessThan(kGrClipEdgeTypeCnt);
-        fp = GrRRectEffect::Make(et, rrect);
+        fp = GrRRectEffect::Make(et, rrect, *d->caps()->shaderCaps());
     } while (nullptr == fp);
     return fp;
 }
@@ -473,7 +473,7 @@
     std::unique_ptr<GrFragmentProcessor> fp;
     do {
         GrClipEdgeType et = (GrClipEdgeType)d->fRandom->nextULessThan(kGrClipEdgeTypeCnt);
-        fp = GrRRectEffect::Make(et, rrect);
+        fp = GrRRectEffect::Make(et, rrect, *d->caps()->shaderCaps());
     } while (nullptr == fp);
     return fp;
 }
@@ -673,13 +673,14 @@
 //////////////////////////////////////////////////////////////////////////////
 
 std::unique_ptr<GrFragmentProcessor> GrRRectEffect::Make(GrClipEdgeType edgeType,
-                                                         const SkRRect& rrect) {
+                                                         const SkRRect& rrect,
+                                                         const GrShaderCaps& caps) {
     if (rrect.isRect()) {
         return GrConvexPolyEffect::Make(edgeType, rrect.getBounds());
     }
 
     if (rrect.isOval()) {
-        return GrOvalEffect::Make(edgeType, rrect.getBounds());
+        return GrOvalEffect::Make(edgeType, rrect.getBounds(), caps);
     }
 
     if (rrect.isSimple()) {
diff --git a/src/gpu/effects/GrRRectEffect.h b/src/gpu/effects/GrRRectEffect.h
index e9c593c..55ef535 100644
--- a/src/gpu/effects/GrRRectEffect.h
+++ b/src/gpu/effects/GrRRectEffect.h
@@ -13,6 +13,7 @@
 #include "SkRefCnt.h"
 
 class GrFragmentProcessor;
+class GrShaderCaps;
 class GrProcessor;
 class SkRRect;
 
@@ -22,8 +23,7 @@
  * Creates an effect that performs anti-aliased clipping against a SkRRect. It doesn't support
  * all varieties of SkRRect so the caller must check for a nullptr return.
  */
-std::unique_ptr<GrFragmentProcessor> Make(GrClipEdgeType, const SkRRect&);
-
+std::unique_ptr<GrFragmentProcessor> Make(GrClipEdgeType, const SkRRect&, const GrShaderCaps&);
 };
 
 #endif
diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp
index 5543623..fe24bc1 100644
--- a/tests/ClipStackTest.cpp
+++ b/tests/ClipStackTest.cpp
@@ -1019,6 +1019,9 @@
             }
         }
 
+        auto context = GrContext::MakeMock(nullptr);
+        const auto* caps = context->caps()->shaderCaps();
+
         // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID
         // will be kInvalidGenID if left uninitialized.
         SkAlignedSTStorage<1, GrReducedClip> storage;
@@ -1028,7 +1031,7 @@
         // Get the reduced version of the stack.
         SkRect queryBounds = kBounds;
         queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
-        const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds);
+        const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds, caps);
 
         REPORTER_ASSERT_MESSAGE(reporter,
                                 reduced->maskElements().isEmpty() ||
@@ -1087,10 +1090,13 @@
                        kReplace_SkClipOp, true);
         SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
 
+        auto context = GrContext::MakeMock(nullptr);
+        const auto* caps = context->caps()->shaderCaps();
+
         SkAlignedSTStorage<1, GrReducedClip> storage;
         memset(storage.get(), 0, sizeof(GrReducedClip));
         GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
-        const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds);
+        const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds, caps);
 
         REPORTER_ASSERT(reporter, reduced->maskElements().count() == 1);
         // Clips will be cached based on the generation id. Make sure the gen id is valid.
@@ -1173,9 +1179,11 @@
 
 #undef XYWH
 #undef IXYWH
+        auto context = GrContext::MakeMock(nullptr);
+        const auto* caps = context->caps()->shaderCaps();
 
         for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
-            const GrReducedClip reduced(stack, testCases[i].testBounds);
+            const GrReducedClip reduced(stack, testCases[i].testBounds, caps);
             REPORTER_ASSERT(reporter, reduced.maskElements().count() ==
                             testCases[i].reducedClipCount);
             SkASSERT(reduced.maskElements().count() == testCases[i].reducedClipCount);
@@ -1199,8 +1207,11 @@
     stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), kReplace_SkClipOp);
     SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
 
+    auto context = GrContext::MakeMock(nullptr);
+    const auto* caps = context->caps()->shaderCaps();
+
     // At the time, this would crash.
-    const GrReducedClip reduced(stack, bounds);
+    const GrReducedClip reduced(stack, bounds, caps);
     REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
 }
 
@@ -1215,9 +1226,12 @@
                           const SkClipStack& stack, const SkMatrix& queryXform,
                           const SkRect& preXformQuery, ClipMethod expectedMethod,
                           int numExpectedElems = 0) {
+    auto context = GrContext::MakeMock(nullptr);
+    const auto* caps = context->caps()->shaderCaps();
+
     SkRect queryBounds;
     queryXform.mapRect(&queryBounds, preXformQuery);
-    const GrReducedClip reduced(stack, queryBounds);
+    const GrReducedClip reduced(stack, queryBounds, caps);
 
     SkClipStack::BoundsType stackBoundsType;
     SkRect stackBounds;
@@ -1377,12 +1391,15 @@
     SkClipStack pathStack;
     pathStack.clipPath(clipPath, SkMatrix::I(), kIntersect_SkClipOp, true);
 
+    auto context = GrContext::MakeMock(nullptr);
+    const auto* caps = context->caps()->shaderCaps();
+
     for (const SkClipStack& stack : {rectStack, pathStack}) {
         for (SkRect queryBounds : {SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance, 1000),
                                    SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance/2, 1000),
                                    SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance),
                                    SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance/2)}) {
-            const GrReducedClip reduced(stack, queryBounds);
+            const GrReducedClip reduced(stack, queryBounds, caps);
             REPORTER_ASSERT(reporter, !reduced.hasScissor());
             REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
             REPORTER_ASSERT(reporter,