• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   * Copyright 2018 Google Inc.
3   *
4   * Use of this source code is governed by a BSD-style license that can be
5   * found in the LICENSE file.
6   */
7  
8  #include "src/gpu/ops/GrFillRRectOp.h"
9  
10  #include "include/private/GrRecordingContext.h"
11  #include "src/core/SkRRectPriv.h"
12  #include "src/gpu/GrCaps.h"
13  #include "src/gpu/GrMemoryPool.h"
14  #include "src/gpu/GrOpFlushState.h"
15  #include "src/gpu/GrOpsRenderPass.h"
16  #include "src/gpu/GrProgramInfo.h"
17  #include "src/gpu/GrRecordingContextPriv.h"
18  #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
19  #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
20  #include "src/gpu/glsl/GrGLSLVarying.h"
21  #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
22  
23  // Hardware derivatives are not always accurate enough for highly elliptical corners. This method
24  // checks to make sure the corners will still all look good if we use HW derivatives.
25  static bool can_use_hw_derivatives_with_coverage(
26          const GrShaderCaps&, const SkMatrix&, const SkRRect&);
27  
Make(GrRecordingContext * ctx,GrAAType aaType,const SkMatrix & viewMatrix,const SkRRect & rrect,const GrCaps & caps,GrPaint && paint)28  std::unique_ptr<GrFillRRectOp> GrFillRRectOp::Make(
29          GrRecordingContext* ctx, GrAAType aaType, const SkMatrix& viewMatrix, const SkRRect& rrect,
30          const GrCaps& caps, GrPaint&& paint) {
31      if (!caps.instanceAttribSupport()) {
32          return nullptr;
33      }
34  
35      Flags flags = Flags::kNone;
36      if (GrAAType::kCoverage == aaType) {
37          // TODO: Support perspective in a follow-on CL. This shouldn't be difficult, since we
38          // already use HW derivatives. The only trick will be adjusting the AA outset to account for
39          // perspective. (i.e., outset = 0.5 * z.)
40          if (viewMatrix.hasPerspective()) {
41              return nullptr;
42          }
43          if (can_use_hw_derivatives_with_coverage(*caps.shaderCaps(), viewMatrix, rrect)) {
44              // HW derivatives (more specifically, fwidth()) are consistently faster on all platforms
45              // in coverage mode. We use them as long as the approximation will be accurate enough.
46              flags |= Flags::kUseHWDerivatives;
47          }
48      } else {
49          if (GrAAType::kMSAA == aaType) {
50              if (!caps.sampleLocationsSupport() || !caps.shaderCaps()->sampleMaskSupport() ||
51                  caps.shaderCaps()->canOnlyUseSampleMaskWithStencil()) {
52                  return nullptr;
53              }
54          }
55          if (viewMatrix.hasPerspective()) {
56              // HW derivatives are consistently slower on all platforms in sample mask mode. We
57              // therefore only use them when there is perspective, since then we can't interpolate
58              // the symbolic screen-space gradient.
59              flags |= Flags::kUseHWDerivatives | Flags::kHasPerspective;
60          }
61      }
62  
63      // Produce a matrix that draws the round rect from normalized [-1, -1, +1, +1] space.
64      float l = rrect.rect().left(), r = rrect.rect().right(),
65            t = rrect.rect().top(), b = rrect.rect().bottom();
66      SkMatrix m;
67      // Unmap the normalized rect [-1, -1, +1, +1] back to [l, t, r, b].
68      m.setScaleTranslate((r - l)/2, (b - t)/2, (l + r)/2, (t + b)/2);
69      // Map to device space.
70      m.postConcat(viewMatrix);
71  
72      SkRect devBounds;
73      if (!(flags & Flags::kHasPerspective)) {
74          // Since m is an affine matrix that maps the rect [-1, -1, +1, +1] into the shape's
75          // device-space quad, it's quite simple to find the bounding rectangle:
76          devBounds = SkRect::MakeXYWH(m.getTranslateX(), m.getTranslateY(), 0, 0);
77          devBounds.outset(SkScalarAbs(m.getScaleX()) + SkScalarAbs(m.getSkewX()),
78                           SkScalarAbs(m.getSkewY()) + SkScalarAbs(m.getScaleY()));
79      } else {
80          viewMatrix.mapRect(&devBounds, rrect.rect());
81      }
82  
83      if (GrAAType::kMSAA == aaType && caps.preferTrianglesOverSampleMask()) {
84          // We are on a platform that prefers fine triangles instead of using the sample mask. See if
85          // the round rect is large enough that it will be faster for us to send it off to the
86          // default path renderer instead. The 200x200 threshold was arrived at using the
87          // "shapes_rrect" benchmark on an ARM Galaxy S9.
88          if (devBounds.height() * devBounds.width() > 200 * 200) {
89              return nullptr;
90          }
91      }
92  
93      GrOpMemoryPool* pool = ctx->priv().opMemoryPool();
94      return pool->allocate<GrFillRRectOp>(aaType, rrect, flags, m, std::move(paint), devBounds);
95  }
96  
GrFillRRectOp(GrAAType aaType,const SkRRect & rrect,Flags flags,const SkMatrix & totalShapeMatrix,GrPaint && paint,const SkRect & devBounds)97  GrFillRRectOp::GrFillRRectOp(GrAAType aaType, const SkRRect& rrect, Flags flags,
98                               const SkMatrix& totalShapeMatrix, GrPaint&& paint,
99                               const SkRect& devBounds)
100          : GrDrawOp(ClassID())
101          , fAAType(aaType)
102          , fOriginalColor(paint.getColor4f())
103          , fLocalRect(rrect.rect())
104          , fFlags(flags)
105          , fProcessors(std::move(paint)) {
106      SkASSERT((fFlags & Flags::kHasPerspective) == totalShapeMatrix.hasPerspective());
107      this->setBounds(devBounds, GrOp::HasAABloat::kYes, GrOp::IsHairline::kNo);
108  
109      // Write the matrix attribs.
110      const SkMatrix& m = totalShapeMatrix;
111      if (!(fFlags & Flags::kHasPerspective)) {
112          // Affine 2D transformation (float2x2 plus float2 translate).
113          SkASSERT(!m.hasPerspective());
114          this->writeInstanceData(m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY());
115          this->writeInstanceData(m.getTranslateX(), m.getTranslateY());
116      } else {
117          // Perspective float3x3 transformation matrix.
118          SkASSERT(m.hasPerspective());
119          m.get9(this->appendInstanceData<float>(9));
120      }
121  
122      // Convert the radii to [-1, -1, +1, +1] space and write their attribs.
123      Sk4f radiiX, radiiY;
124      Sk4f::Load2(SkRRectPriv::GetRadiiArray(rrect), &radiiX, &radiiY);
125      (radiiX * (2/rrect.width())).store(this->appendInstanceData<float>(4));
126      (radiiY * (2/rrect.height())).store(this->appendInstanceData<float>(4));
127  
128      // We will write the color and local rect attribs during finalize().
129  }
130  
finalize(const GrCaps & caps,const GrAppliedClip * clip,bool hasMixedSampledCoverage,GrClampType clampType)131  GrProcessorSet::Analysis GrFillRRectOp::finalize(
132          const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
133          GrClampType clampType) {
134      SkASSERT(1 == fInstanceCount);
135  
136      SkPMColor4f overrideColor;
137      const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
138              fOriginalColor, GrProcessorAnalysisCoverage::kSingleChannel, clip,
139              &GrUserStencilSettings::kUnused, hasMixedSampledCoverage, caps, clampType,
140              &overrideColor);
141  
142      // Finish writing the instance attribs.
143      SkPMColor4f finalColor = analysis.inputColorIsOverridden() ? overrideColor : fOriginalColor;
144      if (!SkPMColor4fFitsInBytes(finalColor)) {
145          fFlags |= Flags::kWideColor;
146          this->writeInstanceData(finalColor);
147      } else {
148          this->writeInstanceData(finalColor.toBytes_RGBA());
149      }
150  
151      if (analysis.usesLocalCoords()) {
152          this->writeInstanceData(fLocalRect);
153          fFlags |= Flags::kHasLocalCoords;
154      }
155      fInstanceStride = fInstanceData.count();
156  
157      return analysis;
158  }
159  
onCombineIfPossible(GrOp * op,GrRecordingContext::Arenas *,const GrCaps &)160  GrDrawOp::CombineResult GrFillRRectOp::onCombineIfPossible(GrOp* op, GrRecordingContext::Arenas*,
161                                                             const GrCaps&) {
162      const auto& that = *op->cast<GrFillRRectOp>();
163      if (fFlags != that.fFlags || fProcessors != that.fProcessors ||
164          fInstanceData.count() > std::numeric_limits<int>::max() - that.fInstanceData.count()) {
165          return CombineResult::kCannotCombine;
166      }
167  
168      fInstanceData.push_back_n(that.fInstanceData.count(), that.fInstanceData.begin());
169      fInstanceCount += that.fInstanceCount;
170      SkASSERT(fInstanceStride == that.fInstanceStride);
171      return CombineResult::kMerged;
172  }
173  
174  class GrFillRRectOp::Processor : public GrGeometryProcessor {
175  public:
Make(SkArenaAlloc * arena,GrAAType aaType,Flags flags)176      static GrGeometryProcessor* Make(SkArenaAlloc* arena, GrAAType aaType, Flags flags) {
177          return arena->make<Processor>(aaType, flags);
178      }
179  
name() const180      const char* name() const final { return "GrFillRRectOp::Processor"; }
181  
getGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const182      void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const final {
183          b->add32(((uint32_t)fFlags << 16) | (uint32_t)fAAType);
184      }
185  
186      GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
187  
188  private:
189      friend class ::SkArenaAlloc; // for access to ctor
190  
Processor(GrAAType aaType,Flags flags)191      Processor(GrAAType aaType, Flags flags)
192              : INHERITED(kGrFillRRectOp_Processor_ClassID)
193              , fAAType(aaType)
194              , fFlags(flags) {
195          int numVertexAttribs = (GrAAType::kCoverage == fAAType) ? 3 : 2;
196          this->setVertexAttributes(kVertexAttribs, numVertexAttribs);
197  
198          if (!(flags & Flags::kHasPerspective)) {
199              // Affine 2D transformation (float2x2 plus float2 translate).
200              fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
201              fInstanceAttribs.emplace_back(
202                      "translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
203          } else {
204              // Perspective float3x3 transformation matrix.
205              fInstanceAttribs.emplace_back("persp_x", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
206              fInstanceAttribs.emplace_back("persp_y", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
207              fInstanceAttribs.emplace_back("persp_z", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
208          }
209          fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
210          fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
211          fColorAttrib = &fInstanceAttribs.push_back(
212                  MakeColorAttribute("color", (flags & Flags::kWideColor)));
213          if (fFlags & Flags::kHasLocalCoords) {
214              fInstanceAttribs.emplace_back(
215                      "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
216          }
217          this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
218  
219          if (GrAAType::kMSAA == fAAType) {
220              this->setWillUseCustomFeature(CustomFeatures::kSampleLocations);
221          }
222      }
223  
224      static constexpr Attribute kVertexAttribs[] = {
225              {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
226              {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
227              // Coverage only.
228              {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
229  
230      const GrAAType fAAType;
231      const Flags fFlags;
232  
233      SkSTArray<6, Attribute> fInstanceAttribs;
234      const Attribute* fColorAttrib;
235  
236      class CoverageImpl;
237      class MSAAImpl;
238  
239      typedef GrGeometryProcessor INHERITED;
240  };
241  
242  constexpr GrPrimitiveProcessor::Attribute GrFillRRectOp::Processor::kVertexAttribs[];
243  
244  // Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
245  // coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
246  // edges. The Vertex struct tells the shader where to place its vertex within a normalized
247  // ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
248  struct CoverageVertex {
249      std::array<float, 4> fRadiiSelector;
250      std::array<float, 2> fCorner;
251      std::array<float, 2> fRadiusOutset;
252      std::array<float, 2> fAABloatDirection;
253      float fCoverage;
254      float fIsLinearCoverage;
255  };
256  
257  // This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
258  // of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
259  // rectangles.
260  static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
261  
262  static constexpr CoverageVertex kCoverageVertexData[] = {
263          // Left inset edge.
264          {{{0,0,0,1}},  {{-1,+1}},  {{0,-1}},  {{+1,0}},  1,  1},
265          {{{1,0,0,0}},  {{-1,-1}},  {{0,+1}},  {{+1,0}},  1,  1},
266  
267          // Top inset edge.
268          {{{1,0,0,0}},  {{-1,-1}},  {{+1,0}},  {{0,+1}},  1,  1},
269          {{{0,1,0,0}},  {{+1,-1}},  {{-1,0}},  {{0,+1}},  1,  1},
270  
271          // Right inset edge.
272          {{{0,1,0,0}},  {{+1,-1}},  {{0,+1}},  {{-1,0}},  1,  1},
273          {{{0,0,1,0}},  {{+1,+1}},  {{0,-1}},  {{-1,0}},  1,  1},
274  
275          // Bottom inset edge.
276          {{{0,0,1,0}},  {{+1,+1}},  {{-1,0}},  {{0,-1}},  1,  1},
277          {{{0,0,0,1}},  {{-1,+1}},  {{+1,0}},  {{0,-1}},  1,  1},
278  
279  
280          // Left outset edge.
281          {{{0,0,0,1}},  {{-1,+1}},  {{0,-1}},  {{-1,0}},  0,  1},
282          {{{1,0,0,0}},  {{-1,-1}},  {{0,+1}},  {{-1,0}},  0,  1},
283  
284          // Top outset edge.
285          {{{1,0,0,0}},  {{-1,-1}},  {{+1,0}},  {{0,-1}},  0,  1},
286          {{{0,1,0,0}},  {{+1,-1}},  {{-1,0}},  {{0,-1}},  0,  1},
287  
288          // Right outset edge.
289          {{{0,1,0,0}},  {{+1,-1}},  {{0,+1}},  {{+1,0}},  0,  1},
290          {{{0,0,1,0}},  {{+1,+1}},  {{0,-1}},  {{+1,0}},  0,  1},
291  
292          // Bottom outset edge.
293          {{{0,0,1,0}},  {{+1,+1}},  {{-1,0}},  {{0,+1}},  0,  1},
294          {{{0,0,0,1}},  {{-1,+1}},  {{+1,0}},  {{0,+1}},  0,  1},
295  
296  
297          // Top-left corner.
298          {{{1,0,0,0}},  {{-1,-1}},  {{ 0,+1}},  {{-1, 0}},  0,  0},
299          {{{1,0,0,0}},  {{-1,-1}},  {{ 0,+1}},  {{+1, 0}},  1,  0},
300          {{{1,0,0,0}},  {{-1,-1}},  {{+1, 0}},  {{ 0,+1}},  1,  0},
301          {{{1,0,0,0}},  {{-1,-1}},  {{+1, 0}},  {{ 0,-1}},  0,  0},
302          {{{1,0,0,0}},  {{-1,-1}},  {{+kOctoOffset,0}},  {{-1,-1}},  0,  0},
303          {{{1,0,0,0}},  {{-1,-1}},  {{0,+kOctoOffset}},  {{-1,-1}},  0,  0},
304  
305          // Top-right corner.
306          {{{0,1,0,0}},  {{+1,-1}},  {{-1, 0}},  {{ 0,-1}},  0,  0},
307          {{{0,1,0,0}},  {{+1,-1}},  {{-1, 0}},  {{ 0,+1}},  1,  0},
308          {{{0,1,0,0}},  {{+1,-1}},  {{ 0,+1}},  {{-1, 0}},  1,  0},
309          {{{0,1,0,0}},  {{+1,-1}},  {{ 0,+1}},  {{+1, 0}},  0,  0},
310          {{{0,1,0,0}},  {{+1,-1}},  {{0,+kOctoOffset}},  {{+1,-1}},  0,  0},
311          {{{0,1,0,0}},  {{+1,-1}},  {{-kOctoOffset,0}},  {{+1,-1}},  0,  0},
312  
313          // Bottom-right corner.
314          {{{0,0,1,0}},  {{+1,+1}},  {{ 0,-1}},  {{+1, 0}},  0,  0},
315          {{{0,0,1,0}},  {{+1,+1}},  {{ 0,-1}},  {{-1, 0}},  1,  0},
316          {{{0,0,1,0}},  {{+1,+1}},  {{-1, 0}},  {{ 0,-1}},  1,  0},
317          {{{0,0,1,0}},  {{+1,+1}},  {{-1, 0}},  {{ 0,+1}},  0,  0},
318          {{{0,0,1,0}},  {{+1,+1}},  {{-kOctoOffset,0}},  {{+1,+1}},  0,  0},
319          {{{0,0,1,0}},  {{+1,+1}},  {{0,-kOctoOffset}},  {{+1,+1}},  0,  0},
320  
321          // Bottom-left corner.
322          {{{0,0,0,1}},  {{-1,+1}},  {{+1, 0}},  {{ 0,+1}},  0,  0},
323          {{{0,0,0,1}},  {{-1,+1}},  {{+1, 0}},  {{ 0,-1}},  1,  0},
324          {{{0,0,0,1}},  {{-1,+1}},  {{ 0,-1}},  {{+1, 0}},  1,  0},
325          {{{0,0,0,1}},  {{-1,+1}},  {{ 0,-1}},  {{-1, 0}},  0,  0},
326          {{{0,0,0,1}},  {{-1,+1}},  {{0,-kOctoOffset}},  {{-1,+1}},  0,  0},
327          {{{0,0,0,1}},  {{-1,+1}},  {{+kOctoOffset,0}},  {{-1,+1}},  0,  0}};
328  
329  GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
330  
331  static constexpr uint16_t kCoverageIndexData[] = {
332          // Inset octagon (solid coverage).
333          0, 1, 7,
334          1, 2, 7,
335          7, 2, 6,
336          2, 3, 6,
337          6, 3, 5,
338          3, 4, 5,
339  
340          // AA borders (linear coverage).
341          0, 1, 8, 1, 9, 8,
342          2, 3, 10, 3, 11, 10,
343          4, 5, 12, 5, 13, 12,
344          6, 7, 14, 7, 15, 14,
345  
346          // Top-left arc.
347          16, 17, 21,
348          17, 21, 18,
349          21, 18, 20,
350          18, 20, 19,
351  
352          // Top-right arc.
353          22, 23, 27,
354          23, 27, 24,
355          27, 24, 26,
356          24, 26, 25,
357  
358          // Bottom-right arc.
359          28, 29, 33,
360          29, 33, 30,
361          33, 30, 32,
362          30, 32, 31,
363  
364          // Bottom-left arc.
365          34, 35, 39,
366          35, 39, 36,
367          39, 36, 38,
368          36, 38, 37};
369  
370  GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
371  
372  
373  // Our MSAA geometry consists of an inset octagon with full sample mask coverage, circumscribed
374  // by a larger octagon that modifies the sample mask for the arc at each corresponding corner.
375  struct MSAAVertex {
376      std::array<float, 4> fRadiiSelector;
377      std::array<float, 2> fCorner;
378      std::array<float, 2> fRadiusOutset;
379  };
380  
381  static constexpr MSAAVertex kMSAAVertexData[] = {
382          // Left edge. (Negative radii selector indicates this is not an arc section.)
383          {{{0,0,0,-1}},  {{-1,+1}},  {{0,-1}}},
384          {{{-1,0,0,0}},  {{-1,-1}},  {{0,+1}}},
385  
386          // Top edge.
387          {{{-1,0,0,0}},  {{-1,-1}},  {{+1,0}}},
388          {{{0,-1,0,0}},  {{+1,-1}},  {{-1,0}}},
389  
390          // Right edge.
391          {{{0,-1,0,0}},  {{+1,-1}},  {{0,+1}}},
392          {{{0,0,-1,0}},  {{+1,+1}},  {{0,-1}}},
393  
394          // Bottom edge.
395          {{{0,0,-1,0}},  {{+1,+1}},  {{-1,0}}},
396          {{{0,0,0,-1}},  {{-1,+1}},  {{+1,0}}},
397  
398          // Top-left corner.
399          {{{1,0,0,0}},  {{-1,-1}},  {{0,+1}}},
400          {{{1,0,0,0}},  {{-1,-1}},  {{0,+kOctoOffset}}},
401          {{{1,0,0,0}},  {{-1,-1}},  {{+1,0}}},
402          {{{1,0,0,0}},  {{-1,-1}},  {{+kOctoOffset,0}}},
403  
404          // Top-right corner.
405          {{{0,1,0,0}},  {{+1,-1}},  {{-1,0}}},
406          {{{0,1,0,0}},  {{+1,-1}},  {{-kOctoOffset,0}}},
407          {{{0,1,0,0}},  {{+1,-1}},  {{0,+1}}},
408          {{{0,1,0,0}},  {{+1,-1}},  {{0,+kOctoOffset}}},
409  
410          // Bottom-right corner.
411          {{{0,0,1,0}},  {{+1,+1}},  {{0,-1}}},
412          {{{0,0,1,0}},  {{+1,+1}},  {{0,-kOctoOffset}}},
413          {{{0,0,1,0}},  {{+1,+1}},  {{-1,0}}},
414          {{{0,0,1,0}},  {{+1,+1}},  {{-kOctoOffset,0}}},
415  
416          // Bottom-left corner.
417          {{{0,0,0,1}},  {{-1,+1}},  {{+1,0}}},
418          {{{0,0,0,1}},  {{-1,+1}},  {{+kOctoOffset,0}}},
419          {{{0,0,0,1}},  {{-1,+1}},  {{0,-1}}},
420          {{{0,0,0,1}},  {{-1,+1}},  {{0,-kOctoOffset}}}};
421  
422  GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
423  
424  static constexpr uint16_t kMSAAIndexData[] = {
425          // Inset octagon. (Full sample mask.)
426          0, 1, 2,
427          0, 2, 3,
428          0, 3, 6,
429          3, 4, 5,
430          3, 5, 6,
431          6, 7, 0,
432  
433          // Top-left arc. (Sample mask is set to the arc.)
434           8,  9, 10,
435           9, 11, 10,
436  
437          // Top-right arc.
438          12, 13, 14,
439          13, 15, 14,
440  
441          // Bottom-right arc.
442          16, 17, 18,
443          17, 19, 18,
444  
445          // Bottom-left arc.
446          20, 21, 22,
447          21, 23, 22};
448  
449  GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
450  
onPrePrepare(GrRecordingContext * context,const GrSurfaceProxyView * dstView,GrAppliedClip * clip,const GrXferProcessor::DstProxyView & dstProxyView)451  void GrFillRRectOp::onPrePrepare(GrRecordingContext* context,
452                                   const GrSurfaceProxyView* dstView,
453                                   GrAppliedClip* clip,
454                                   const GrXferProcessor::DstProxyView& dstProxyView) {
455      SkArenaAlloc* arena = context->priv().recordTimeAllocator();
456  
457      // This is equivalent to a GrOpFlushState::detachAppliedClip
458      GrAppliedClip appliedClip = clip ? std::move(*clip) : GrAppliedClip();
459  
460      // TODO: it would be cool if, right here, we created both the program info and desc
461      // in the record-time arena. Then, if the program info had already been seen, we could
462      // get pointers back to the prior versions and be able to return the allocated space
463      // back to the arena.
464      fProgramInfo = this->createProgramInfo(context->priv().caps(), arena, dstView,
465                                             std::move(appliedClip), dstProxyView);
466  
467      context->priv().recordProgramInfo(fProgramInfo);
468  }
469  
onPrepare(GrOpFlushState * flushState)470  void GrFillRRectOp::onPrepare(GrOpFlushState* flushState) {
471      if (void* instanceData = flushState->makeVertexSpace(fInstanceStride, fInstanceCount,
472                                                           &fInstanceBuffer, &fBaseInstance)) {
473          SkASSERT(fInstanceStride * fInstanceCount == fInstanceData.count());
474          memcpy(instanceData, fInstanceData.begin(), fInstanceData.count());
475      }
476  
477      if (GrAAType::kCoverage == fAAType) {
478          GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
479  
480          fIndexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
481                  GrGpuBufferType::kIndex, sizeof(kCoverageIndexData), kCoverageIndexData,
482                  gCoverageIndexBufferKey);
483  
484          GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
485  
486          fVertexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
487                  GrGpuBufferType::kVertex, sizeof(kCoverageVertexData), kCoverageVertexData,
488                  gCoverageVertexBufferKey);
489  
490          fIndexCount = SK_ARRAY_COUNT(kCoverageIndexData);
491      } else {
492          GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
493  
494          fIndexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
495                  GrGpuBufferType::kIndex, sizeof(kMSAAIndexData), kMSAAIndexData,
496                  gMSAAIndexBufferKey);
497  
498          GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
499  
500          fVertexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
501                  GrGpuBufferType::kVertex, sizeof(kMSAAVertexData), kMSAAVertexData,
502                  gMSAAVertexBufferKey);
503  
504          fIndexCount = SK_ARRAY_COUNT(kMSAAIndexData);
505      }
506  }
507  
508  class GrFillRRectOp::Processor::CoverageImpl : public GrGLSLGeometryProcessor {
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)509      void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
510          const auto& proc = args.fGP.cast<Processor>();
511          bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives);
512  
513          SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
514  
515          GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
516          varyings->emitAttributes(proc);
517          varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
518                                            GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
519  
520          // Emit the vertex shader.
521          GrGLSLVertexBuilder* v = args.fVertBuilder;
522  
523          // Unpack vertex attribs.
524          v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
525          v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
526          v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
527          v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
528          v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
529  
530          // Find the amount to bloat each edge for AA (in source space).
531          v->codeAppend("float2 pixellength = inversesqrt("
532                                "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
533          v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
534          v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
535                                             "abs(normalized_axis_dirs.zw));");
536          v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
537  
538          // Identify our radii.
539          v->codeAppend("float4 radii_and_neighbors = radii_selector"
540                                "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
541          v->codeAppend("float2 radii = radii_and_neighbors.xy;");
542          v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
543  
544          v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
545                            // The rrect is more narrow than an AA coverage ramp. We can't draw as-is
546                            // or else opposite AA borders will overlap. Instead, fudge the size up to
547                            // the width of a coverage ramp, and then reduce total coverage to make
548                            // the rect appear more thin.
549          v->codeAppend(    "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
550          v->codeAppend(    "coverage /= max(aa_bloatradius.x, 1) * max(aa_bloatradius.y, 1);");
551                            // Set radii to zero to ensure we take the "linear coverage" codepath.
552                            // (The "coverage" variable only has effect in the linear codepath.)
553          v->codeAppend(    "radii = float2(0);");
554          v->codeAppend("}");
555  
556          v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.25))) {");
557                            // The radii are very small. Demote this arc to a sharp 90 degree corner.
558          v->codeAppend(    "radii = aa_bloatradius;");
559                            // Snap octagon vertices to the corner of the bounding box.
560          v->codeAppend(    "radius_outset = floor(abs(radius_outset)) * radius_outset;");
561          v->codeAppend(    "is_linear_coverage = 1;");
562          v->codeAppend("} else {");
563                            // Don't let radii get smaller than a pixel.
564          v->codeAppend(    "radii = clamp(radii, pixellength, 2 - pixellength);");
565          v->codeAppend(    "neighbor_radii = clamp(neighbor_radii, pixellength, 2 - pixellength);");
566                            // Don't let neighboring radii get closer together than 1/16 pixel.
567          v->codeAppend(    "float2 spacing = 2 - radii - neighbor_radii;");
568          v->codeAppend(    "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
569          v->codeAppend(    "radii -= extra_pad * .5;");
570          v->codeAppend("}");
571  
572          // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
573          // normalized [-1,-1,+1,+1] space.
574          v->codeAppend("float2 aa_outset = aa_bloat_direction.xy * aa_bloatradius;");
575          v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
576  
577          // Emit transforms.
578          GrShaderVar localCoord("", kFloat2_GrSLType);
579          if (proc.fFlags & Flags::kHasLocalCoords) {
580              v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
581                                                 "local_rect.zw * (1 + vertexpos)) * .5;");
582              localCoord.set(kFloat2_GrSLType, "localcoord");
583          }
584          this->emitTransforms(v, varyings, args.fUniformHandler, localCoord,
585                               args.fFPCoordTransformHandler);
586  
587          // Transform to device space.
588          SkASSERT(!(proc.fFlags & Flags::kHasPerspective));
589          v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
590          v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
591          gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
592  
593          // Setup interpolants for coverage.
594          GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
595          varyings->addVarying("arccoord", &arcCoord);
596          v->codeAppend("if (0 != is_linear_coverage) {");
597                             // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
598                             // interpolate linear coverage across y.
599          v->codeAppendf(    "%s.xy = float2(0, coverage);", arcCoord.vsOut());
600          v->codeAppend("} else {");
601                             // Find the normalized arc coordinates for our corner ellipse.
602                             // (i.e., the coordinate system where x^2 + y^2 == 1).
603          v->codeAppend(    "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
604                             // We are a corner piece: Interpolate the arc coordinates for coverage.
605                             // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
606                             // instructs the fragment shader to use linear coverage).
607          v->codeAppendf(    "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
608          if (!useHWDerivatives) {
609              // The gradient is order-1: Interpolate it across arccoord.zw.
610              v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
611              v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
612          }
613          v->codeAppend("}");
614  
615          // Emit the fragment shader.
616          GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
617  
618          f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
619          f->codeAppendf("half coverage;");
620          f->codeAppendf("if (0 == x_plus_1) {");
621          f->codeAppendf(    "coverage = half(y);");  // We are a non-arc pixel (linear coverage).
622          f->codeAppendf("} else {");
623          f->codeAppendf(    "float fn = x_plus_1 * (x_plus_1 - 2);");  // fn = (x+1)*(x-1) = x^2-1
624          f->codeAppendf(    "fn = fma(y,y, fn);");  // fn = x^2 + y^2 - 1
625          if (useHWDerivatives) {
626              f->codeAppendf("float fnwidth = fwidth(fn);");
627          } else {
628              // The gradient is interpolated across arccoord.zw.
629              f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
630              f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
631          }
632          f->codeAppendf(    "half d = half(fn/fnwidth);");
633          f->codeAppendf(    "coverage = clamp(.5 - d, 0, 1);");
634          f->codeAppendf("}");
635          f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage);
636      }
637  
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor &,const CoordTransformRange & transformRange)638      void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
639                   const CoordTransformRange& transformRange) override {
640          this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange);
641      }
642  };
643  
644  
645  class GrFillRRectOp::Processor::MSAAImpl : public GrGLSLGeometryProcessor {
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)646      void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
647          const auto& proc = args.fGP.cast<Processor>();
648          bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives);
649          bool hasPerspective = (proc.fFlags & Flags::kHasPerspective);
650          bool hasLocalCoords = (proc.fFlags & Flags::kHasLocalCoords);
651          SkASSERT(useHWDerivatives == hasPerspective);
652  
653          SkASSERT(proc.vertexStride() == sizeof(MSAAVertex));
654  
655          // Emit the vertex shader.
656          GrGLSLVertexBuilder* v = args.fVertBuilder;
657  
658          GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
659          varyings->emitAttributes(proc);
660          varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
661                                            GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
662  
663          // Unpack vertex attribs.
664          v->codeAppendf("float2 corner = corner_and_radius_outsets.xy;");
665          v->codeAppendf("float2 radius_outset = corner_and_radius_outsets.zw;");
666  
667          // Identify our radii.
668          v->codeAppend("float2 radii;");
669          v->codeAppend("radii.x = dot(radii_selector, radii_x);");
670          v->codeAppend("radii.y = dot(radii_selector, radii_y);");
671          v->codeAppendf("bool is_arc_section = (radii.x > 0);");
672          v->codeAppendf("radii = abs(radii);");
673  
674          // Find our vertex position, adjusted for radii. Our rect is drawn in normalized
675          // [-1,-1,+1,+1] space.
676          v->codeAppend("float2 vertexpos = corner + radius_outset * radii;");
677  
678          // Emit transforms.
679          GrShaderVar localCoord("", kFloat2_GrSLType);
680          if (hasLocalCoords) {
681              v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
682                                                 "local_rect.zw * (1 + vertexpos)) * .5;");
683              localCoord.set(kFloat2_GrSLType, "localcoord");
684          }
685          this->emitTransforms(v, varyings, args.fUniformHandler, localCoord,
686                               args.fFPCoordTransformHandler);
687  
688          // Transform to device space.
689          if (!hasPerspective) {
690              v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
691              v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
692              gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
693          } else {
694              v->codeAppend("float3x3 persp_matrix = float3x3(persp_x, persp_y, persp_z);");
695              v->codeAppend("float3 devcoord = float3(vertexpos, 1) * persp_matrix;");
696              gpArgs->fPositionVar.set(kFloat3_GrSLType, "devcoord");
697          }
698  
699          // Determine normalized arc coordinates for the implicit function.
700          GrGLSLVarying arcCoord((useHWDerivatives) ? kFloat2_GrSLType : kFloat4_GrSLType);
701          varyings->addVarying("arccoord", &arcCoord);
702          v->codeAppendf("if (is_arc_section) {");
703          v->codeAppendf(    "%s.xy = 1 - abs(radius_outset);", arcCoord.vsOut());
704          if (!useHWDerivatives) {
705              // The gradient is order-1: Interpolate it across arccoord.zw.
706              // This doesn't work with perspective.
707              SkASSERT(!hasPerspective);
708              v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
709              v->codeAppendf("%s.zw = derivatives * (%s.xy/radii * corner * 2);",
710                             arcCoord.vsOut(), arcCoord.vsOut());
711          }
712          v->codeAppendf("} else {");
713          if (useHWDerivatives) {
714              v->codeAppendf("%s = float2(0);", arcCoord.vsOut());
715          } else {
716              v->codeAppendf("%s = float4(0);", arcCoord.vsOut());
717          }
718          v->codeAppendf("}");
719  
720          // Emit the fragment shader.
721          GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
722  
723          f->codeAppendf("%s = half4(1);", args.fOutputCoverage);
724  
725          // If x,y == 0, then we are drawing a triangle that does not track an arc.
726          f->codeAppendf("if (float2(0) != %s.xy) {", arcCoord.fsIn());
727          f->codeAppendf(    "float fn = dot(%s.xy, %s.xy) - 1;", arcCoord.fsIn(), arcCoord.fsIn());
728          if (GrAAType::kMSAA == proc.fAAType) {
729              using ScopeFlags = GrGLSLFPFragmentBuilder::ScopeFlags;
730              if (!useHWDerivatives) {
731                  f->codeAppendf("float2 grad = %s.zw;", arcCoord.fsIn());
732                  f->applyFnToMultisampleMask("fn", "grad", ScopeFlags::kInsidePerPrimitiveBranch);
733              } else {
734                  f->applyFnToMultisampleMask("fn", nullptr, ScopeFlags::kInsidePerPrimitiveBranch);
735              }
736          } else {
737              f->codeAppendf("if (fn > 0) {");
738              f->codeAppendf(    "%s = half4(0);", args.fOutputCoverage);
739              f->codeAppendf("}");
740          }
741          f->codeAppendf("}");
742      }
743  
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor &,const CoordTransformRange & transformRange)744      void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
745                   const CoordTransformRange& transformRange) override {
746          this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange);
747      }
748  };
749  
createGLSLInstance(const GrShaderCaps &) const750  GrGLSLPrimitiveProcessor* GrFillRRectOp::Processor::createGLSLInstance(
751          const GrShaderCaps&) const {
752      if (GrAAType::kCoverage != fAAType) {
753          return new MSAAImpl();
754      }
755      return new CoverageImpl();
756  }
757  
createProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView * dstView,GrAppliedClip && appliedClip,const GrXferProcessor::DstProxyView & dstProxyView)758  GrProgramInfo* GrFillRRectOp::createProgramInfo(const GrCaps* caps,
759                                                  SkArenaAlloc* arena,
760                                                  const GrSurfaceProxyView* dstView,
761                                                  GrAppliedClip&& appliedClip,
762                                                  const GrXferProcessor::DstProxyView& dstProxyView) {
763      GrGeometryProcessor* geomProc = Processor::Make(arena, fAAType, fFlags);
764      SkASSERT(geomProc->instanceStride() == (size_t)fInstanceStride);
765  
766      GrPipeline::InitArgs initArgs;
767      if (GrAAType::kMSAA == fAAType) {
768          initArgs.fInputFlags = GrPipeline::InputFlags::kHWAntialias;
769      }
770      initArgs.fCaps = caps;
771      initArgs.fDstProxyView = dstProxyView;
772      initArgs.fOutputSwizzle = dstView->swizzle();
773  
774      GrPipeline::FixedDynamicState* fixedDynamicState = nullptr;
775  
776      if (appliedClip.scissorState().enabled()) {
777          fixedDynamicState = arena->make<GrPipeline::FixedDynamicState>(
778                                                          appliedClip.scissorState().rect());
779      }
780  
781      GrPipeline* pipeline = arena->make<GrPipeline>(initArgs,
782                                                     std::move(fProcessors),
783                                                     std::move(appliedClip));
784  
785      GrRenderTargetProxy* dstProxy = dstView->asRenderTargetProxy();
786      return arena->make<GrProgramInfo>(dstProxy->numSamples(),
787                                        dstProxy->numStencilSamples(),
788                                        dstProxy->backendFormat(),
789                                        dstView->origin(),
790                                        pipeline,
791                                        geomProc,
792                                        fixedDynamicState,
793                                        nullptr, 0,
794                                        GrPrimitiveType::kTriangles);
795  }
796  
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)797  void GrFillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
798      if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
799          return;  // Setup failed.
800      }
801  
802      if (!fProgramInfo) {
803          const GrSurfaceProxyView* dstView = flushState->view();
804  
805          fProgramInfo = this->createProgramInfo(&flushState->caps(),
806                                                 flushState->allocator(),
807                                                 dstView,
808                                                 flushState->detachAppliedClip(),
809                                                 flushState->dstProxyView());
810      }
811  
812      GrMesh* mesh = flushState->allocator()->make<GrMesh>();
813      mesh->setIndexedInstanced(std::move(fIndexBuffer), fIndexCount,
814                                std::move(fInstanceBuffer), fInstanceCount,
815                                fBaseInstance, GrPrimitiveRestart::kNo);
816      mesh->setVertexData(std::move(fVertexBuffer));
817  
818      flushState->opsRenderPass()->bindPipeline(*fProgramInfo, this->bounds());
819      flushState->opsRenderPass()->drawMeshes(*fProgramInfo, mesh, 1);
820  }
821  
822  // Will the given corner look good if we use HW derivatives?
can_use_hw_derivatives_with_coverage(const Sk2f & devScale,const Sk2f & cornerRadii)823  static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
824      Sk2f devRadii = devScale * cornerRadii;
825      if (devRadii[1] < devRadii[0]) {
826          devRadii = SkNx_shuffle<1,0>(devRadii);
827      }
828      float minDevRadius = std::max(devRadii[0], 1.f);  // Shader clamps radius at a minimum of 1.
829      // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
830      // This threshold was arrived at subjevtively on an NVIDIA chip.
831      return minDevRadius * minDevRadius * 5 > devRadii[1];
832  }
833  
can_use_hw_derivatives_with_coverage(const Sk2f & devScale,const SkVector & cornerRadii)834  static bool can_use_hw_derivatives_with_coverage(
835          const Sk2f& devScale, const SkVector& cornerRadii) {
836      return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
837  }
838  
839  // Will the given round rect look good if we use HW derivatives?
can_use_hw_derivatives_with_coverage(const GrShaderCaps & shaderCaps,const SkMatrix & viewMatrix,const SkRRect & rrect)840  static bool can_use_hw_derivatives_with_coverage(
841          const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
842      if (!shaderCaps.shaderDerivativeSupport()) {
843          return false;
844      }
845  
846      Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
847      Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
848      Sk2f devScale = (x*x + y*y).sqrt();
849      switch (rrect.getType()) {
850          case SkRRect::kEmpty_Type:
851          case SkRRect::kRect_Type:
852              return true;
853  
854          case SkRRect::kOval_Type:
855          case SkRRect::kSimple_Type:
856              return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
857  
858          case SkRRect::kNinePatch_Type: {
859              Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
860              Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
861              Sk2f minRadii = Sk2f::Min(r0, r1);
862              Sk2f maxRadii = Sk2f::Max(r0, r1);
863              return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
864                     can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
865          }
866  
867          case SkRRect::kComplex_Type: {
868              for (int i = 0; i < 4; ++i) {
869                  auto corner = static_cast<SkRRect::Corner>(i);
870                  if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
871                      return false;
872                  }
873              }
874              return true;
875          }
876      }
877      SK_ABORT("Invalid round rect type.");
878  }
879