xref: /aosp_15_r20/external/skia/src/gpu/ganesh/ops/FillRRectOp.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/ganesh/ops/FillRRectOp.h"
9 
10 #include "include/core/SkClipOp.h"
11 #include "include/core/SkMatrix.h"
12 #include "include/core/SkRRect.h"
13 #include "include/core/SkRect.h"
14 #include "include/core/SkRefCnt.h"
15 #include "include/core/SkScalar.h"
16 #include "include/core/SkString.h"
17 #include "include/gpu/ganesh/GrRecordingContext.h"
18 #include "include/private/SkColorData.h"
19 #include "include/private/base/SkAlignedStorage.h"
20 #include "include/private/base/SkAssert.h"
21 #include "include/private/base/SkDebug.h"
22 #include "include/private/base/SkMacros.h"
23 #include "include/private/base/SkOnce.h"
24 #include "include/private/base/SkPoint_impl.h"
25 #include "include/private/base/SkTArray.h"
26 #include "include/private/gpu/ganesh/GrTypesPriv.h"
27 #include "src/base/SkArenaAlloc.h"
28 #include "src/base/SkUtils.h"
29 #include "src/base/SkVx.h"
30 #include "src/core/SkRRectPriv.h"
31 #include "src/core/SkSLTypeShared.h"
32 #include "src/gpu/BufferWriter.h"
33 #include "src/gpu/KeyBuilder.h"
34 #include "src/gpu/ResourceKey.h"
35 #include "src/gpu/ganesh/GrAppliedClip.h"
36 #include "src/gpu/ganesh/GrBuffer.h"
37 #include "src/gpu/ganesh/GrCaps.h"
38 #include "src/gpu/ganesh/GrGeometryProcessor.h"
39 #include "src/gpu/ganesh/GrMeshDrawTarget.h"
40 #include "src/gpu/ganesh/GrOpFlushState.h"
41 #include "src/gpu/ganesh/GrPaint.h"
42 #include "src/gpu/ganesh/GrProcessorAnalysis.h"
43 #include "src/gpu/ganesh/GrProcessorSet.h"
44 #include "src/gpu/ganesh/GrProgramInfo.h"
45 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
46 #include "src/gpu/ganesh/GrResourceProvider.h"
47 #include "src/gpu/ganesh/GrShaderCaps.h"
48 #include "src/gpu/ganesh/GrShaderVar.h"
49 #include "src/gpu/ganesh/geometry/GrShape.h"
50 #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
51 #include "src/gpu/ganesh/glsl/GrGLSLVarying.h"
52 #include "src/gpu/ganesh/glsl/GrGLSLVertexGeoBuilder.h"
53 #include "src/gpu/ganesh/ops/GrDrawOp.h"
54 #include "src/gpu/ganesh/ops/GrMeshDrawOp.h"
55 #include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelper.h"
56 
57 #if defined(GPU_TEST_UTILS)
58 #include "src/base/SkRandom.h"
59 #include "src/gpu/ganesh/GrDrawOpTest.h"
60 #include "src/gpu/ganesh/GrTestUtils.h"
61 #endif
62 
63 #include <algorithm>
64 #include <array>
65 #include <cstddef>
66 #include <cstdint>
67 #include <memory>
68 #include <utility>
69 
70 class GrDstProxyView;
71 class GrGLSLProgramDataManager;
72 class GrSurfaceProxyView;
73 enum class GrXferBarrierFlags;
74 
75 namespace skgpu::ganesh {
76 class SurfaceDrawContext;
77 }
78 
79 using namespace skia_private;
80 
81 namespace skgpu::ganesh::FillRRectOp {
82 
83 namespace {
84 
85 // Note: Just checking m.restStaysRect is not sufficient
skews_are_relevant(const SkMatrix & m)86 bool skews_are_relevant(const SkMatrix& m) {
87     SkASSERT(!m.hasPerspective());
88 
89     if (m[SkMatrix::kMSkewX] == 0.0f && m[SkMatrix::kMSkewY] == 0.0f) {
90         return false;
91     }
92 
93     static constexpr float kTol = SK_ScalarNearlyZero;
94     float absScaleX = SkScalarAbs(m[SkMatrix::kMScaleX]);
95     float absSkewX  = SkScalarAbs(m[SkMatrix::kMSkewX]);
96     float absScaleY = SkScalarAbs(m[SkMatrix::kMScaleY]);
97     float absSkewY  = SkScalarAbs(m[SkMatrix::kMSkewY]);
98 
99     // The maximum absolute column sum norm of the upper left 2x2
100     float norm = std::max(absScaleX + absSkewY, absSkewX + absScaleY);
101 
102     return absSkewX > kTol * norm || absSkewY > kTol * norm;
103 }
104 
105 class FillRRectOpImpl final : public GrMeshDrawOp {
106 private:
107     using Helper = GrSimpleMeshDrawOpHelper;
108 
109 public:
110     DEFINE_OP_CLASS_ID
111 
112     struct LocalCoords {
113         enum class Type : bool { kRect, kMatrix };
LocalCoordsskgpu::ganesh::FillRRectOp::__anone688170d0111::FillRRectOpImpl::LocalCoords114         LocalCoords(const SkRect& localRect)
115                 : fType(Type::kRect)
116                 , fRect(localRect) {}
LocalCoordsskgpu::ganesh::FillRRectOp::__anone688170d0111::FillRRectOpImpl::LocalCoords117         LocalCoords(const SkMatrix& localMatrix)
118                 : fType(Type::kMatrix)
119                 , fMatrix(localMatrix) {}
120         Type fType;
121         union {
122             SkRect fRect;
123             SkMatrix fMatrix;
124         };
125     };
126 
127     static GrOp::Owner Make(GrRecordingContext*,
128                             SkArenaAlloc*,
129                             GrPaint&&,
130                             const SkMatrix& viewMatrix,
131                             const SkRRect&,
132                             const LocalCoords&,
133                             GrAA);
134 
name() const135     const char* name() const override { return "FillRRectOp"; }
136 
fixedFunctionFlags() const137     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
138 
139     ClipResult clipToShape(skgpu::ganesh::SurfaceDrawContext*,
140                            SkClipOp,
141                            const SkMatrix& clipMatrix,
142                            const GrShape&,
143                            GrAA) override;
144 
145     GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override;
146     CombineResult onCombineIfPossible(GrOp*, SkArenaAlloc*, const GrCaps&) override;
147 
148 #if defined(GPU_TEST_UTILS)
149     SkString onDumpInfo() const override;
150 #endif
151 
visitProxies(const GrVisitProxyFunc & func) const152     void visitProxies(const GrVisitProxyFunc& func) const override {
153         if (fProgramInfo) {
154             fProgramInfo->visitFPProxies(func);
155         } else {
156             fHelper.visitProxies(func);
157         }
158     }
159 
160     void onPrepareDraws(GrMeshDrawTarget*) override;
161 
162     void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
163 
164 private:
165     friend class ::GrSimpleMeshDrawOpHelper; // for access to ctor
166     friend class ::GrOp;         // for access to ctor
167 
168     enum class ProcessorFlags {
169         kNone             = 0,
170         kUseHWDerivatives = 1 << 0,
171         kHasLocalCoords   = 1 << 1,
172         kWideColor        = 1 << 2,
173         kMSAAEnabled      = 1 << 3,
174         kFakeNonAA        = 1 << 4,
175     };
176     constexpr static int kNumProcessorFlags = 5;
177 
178     SK_DECL_BITFIELD_CLASS_OPS_FRIENDS(ProcessorFlags);
179 
180     class Processor;
181 
182     FillRRectOpImpl(GrProcessorSet*,
183                     const SkPMColor4f& paintColor,
184                     SkArenaAlloc*,
185                     const SkMatrix& viewMatrix,
186                     const SkRRect&,
187                     const LocalCoords&,
188                     ProcessorFlags);
189 
programInfo()190     GrProgramInfo* programInfo() override { return fProgramInfo; }
191 
192     // Create a GrProgramInfo object in the provided arena
193     void onCreateProgramInfo(const GrCaps*,
194                              SkArenaAlloc*,
195                              const GrSurfaceProxyView& writeView,
196                              bool usesMSAASurface,
197                              GrAppliedClip&&,
198                              const GrDstProxyView&,
199                              GrXferBarrierFlags renderPassXferBarriers,
200                              GrLoadOp colorLoadOp) override;
201 
202     Helper         fHelper;
203     ProcessorFlags fProcessorFlags;
204 
205     struct Instance {
Instanceskgpu::ganesh::FillRRectOp::__anone688170d0111::FillRRectOpImpl::Instance206         Instance(const SkMatrix& viewMatrix,
207                  const SkRRect& rrect,
208                  const LocalCoords& localCoords,
209                  const SkPMColor4f& color)
210                 : fViewMatrix(viewMatrix), fRRect(rrect), fLocalCoords(localCoords), fColor(color) {
211         }
212         SkMatrix fViewMatrix;
213         SkRRect fRRect;
214         LocalCoords fLocalCoords;
215         SkPMColor4f fColor;
216         Instance* fNext = nullptr;
217     };
218 
219     Instance* fHeadInstance;
220     Instance** fTailInstance;
221     int fInstanceCount = 1;
222 
223     sk_sp<const GrBuffer> fInstanceBuffer;
224     sk_sp<const GrBuffer> fVertexBuffer;
225     sk_sp<const GrBuffer> fIndexBuffer;
226     int fBaseInstance = 0;
227 
228     // If this op is prePrepared the created programInfo will be stored here for use in
229     // onExecute. In the prePrepared case it will have been stored in the record-time arena.
230     GrProgramInfo* fProgramInfo = nullptr;
231 };
232 
233 SK_MAKE_BITFIELD_CLASS_OPS(FillRRectOpImpl::ProcessorFlags)
234 
235 // Hardware derivatives are not always accurate enough for highly elliptical corners. This method
236 // checks to make sure the corners will still all look good if we use HW derivatives.
237 bool can_use_hw_derivatives_with_coverage(const GrShaderCaps&,
238                                           const SkMatrix&,
239                                           const SkRRect&);
240 
Make(GrRecordingContext * ctx,SkArenaAlloc * arena,GrPaint && paint,const SkMatrix & viewMatrix,const SkRRect & rrect,const LocalCoords & localCoords,GrAA aa)241 GrOp::Owner FillRRectOpImpl::Make(GrRecordingContext* ctx,
242                                   SkArenaAlloc* arena,
243                                   GrPaint&& paint,
244                                   const SkMatrix& viewMatrix,
245                                   const SkRRect& rrect,
246                                   const LocalCoords& localCoords,
247                                   GrAA aa) {
248     const GrCaps* caps = ctx->priv().caps();
249 
250     if (!caps->drawInstancedSupport()) {
251         return nullptr;
252     }
253 
254     // We transform into a normalized -1..+1 space to draw the round rect. If the boundaries are too
255     // large, the math can overflow. The caller can fall back on path rendering if this is the case.
256     if (std::max(rrect.height(), rrect.width()) >= 1e6f) {
257         return nullptr;
258     }
259 
260     ProcessorFlags flags = ProcessorFlags::kNone;
261     // TODO: Support perspective in a follow-on CL. This shouldn't be difficult, since we already
262     // use HW derivatives. The only trick will be adjusting the AA outset to account for
263     // perspective. (i.e., outset = 0.5 * z.)
264     if (viewMatrix.hasPerspective()) {
265         return nullptr;
266     }
267     if (can_use_hw_derivatives_with_coverage(*caps->shaderCaps(), viewMatrix, rrect)) {
268         // HW derivatives (more specifically, fwidth()) are consistently faster on all platforms in
269         // coverage mode. We use them as long as the approximation will be accurate enough.
270         flags |= ProcessorFlags::kUseHWDerivatives;
271     }
272     if (aa == GrAA::kNo) {
273         flags |= ProcessorFlags::kFakeNonAA;
274     }
275 
276     return Helper::FactoryHelper<FillRRectOpImpl>(ctx, std::move(paint), arena, viewMatrix, rrect,
277                                                   localCoords, flags);
278 }
279 
FillRRectOpImpl(GrProcessorSet * processorSet,const SkPMColor4f & paintColor,SkArenaAlloc * arena,const SkMatrix & viewMatrix,const SkRRect & rrect,const LocalCoords & localCoords,ProcessorFlags processorFlags)280 FillRRectOpImpl::FillRRectOpImpl(GrProcessorSet* processorSet,
281                                  const SkPMColor4f& paintColor,
282                                  SkArenaAlloc* arena,
283                                  const SkMatrix& viewMatrix,
284                                  const SkRRect& rrect,
285                                  const LocalCoords& localCoords,
286                                  ProcessorFlags processorFlags)
287         : GrMeshDrawOp(ClassID())
288         , fHelper(processorSet,
289                   (processorFlags & ProcessorFlags::kFakeNonAA)
290                           ? GrAAType::kNone
291                           : GrAAType::kCoverage)  // Use analytic AA even if the RT is MSAA.
292         , fProcessorFlags(processorFlags & ~(ProcessorFlags::kHasLocalCoords |
293                                              ProcessorFlags::kWideColor |
294                                              ProcessorFlags::kMSAAEnabled))
295         , fHeadInstance(arena->make<Instance>(viewMatrix, rrect, localCoords, paintColor))
296         , fTailInstance(&fHeadInstance->fNext) {
297     // FillRRectOp::Make fails if there is perspective.
298     SkASSERT(!viewMatrix.hasPerspective());
299     this->setBounds(viewMatrix.mapRect(rrect.getBounds()),
300                     GrOp::HasAABloat(!(processorFlags & ProcessorFlags::kFakeNonAA)),
301                     GrOp::IsHairline::kNo);
302 }
303 
clipToShape(skgpu::ganesh::SurfaceDrawContext * sdc,SkClipOp clipOp,const SkMatrix & clipMatrix,const GrShape & shape,GrAA aa)304 GrDrawOp::ClipResult FillRRectOpImpl::clipToShape(skgpu::ganesh::SurfaceDrawContext* sdc,
305                                                   SkClipOp clipOp,
306                                                   const SkMatrix& clipMatrix,
307                                                   const GrShape& shape,
308                                                   GrAA aa) {
309     SkASSERT(fInstanceCount == 1);  // This needs to be called before combining.
310     SkASSERT(fHeadInstance->fNext == nullptr);
311 
312     if ((shape.isRect() || shape.isRRect()) &&
313         clipOp == SkClipOp::kIntersect &&
314         (aa == GrAA::kNo) == (fProcessorFlags & ProcessorFlags::kFakeNonAA)) {
315         // The clip shape is a round rect. Attempt to map it to a round rect in "viewMatrix" space.
316         SkRRect clipRRect;
317         if (clipMatrix == fHeadInstance->fViewMatrix) {
318             if (shape.isRect()) {
319                 clipRRect.setRect(shape.rect());
320             } else {
321                 clipRRect = shape.rrect();
322             }
323         } else {
324             // Find a matrix that maps from "clipMatrix" space to "viewMatrix" space.
325             SkASSERT(!fHeadInstance->fViewMatrix.hasPerspective());
326             if (clipMatrix.hasPerspective()) {
327                 return ClipResult::kFail;
328             }
329             SkMatrix clipToView;
330             if (!fHeadInstance->fViewMatrix.invert(&clipToView)) {
331                 return ClipResult::kClippedOut;
332             }
333             clipToView.preConcat(clipMatrix);
334             SkASSERT(!clipToView.hasPerspective());
335 
336             if (skews_are_relevant(clipToView)) {
337                 // A rect in "clipMatrix" space is not a rect in "viewMatrix" space.
338                 return ClipResult::kFail;
339             }
340             clipToView.setSkewX(0);
341             clipToView.setSkewY(0);
342             SkASSERT(clipToView.rectStaysRect());
343 
344             if (shape.isRect()) {
345                 clipRRect.setRect(clipToView.mapRect(shape.rect()));
346             } else {
347                 if (!shape.rrect().transform(clipToView, &clipRRect)) {
348                     // Transforming the rrect failed. This shouldn't generally happen except in
349                     // cases of fp32 overflow.
350                     return ClipResult::kFail;
351                 }
352             }
353         }
354 
355         // Intersect our round rect with the clip shape.
356         SkRRect isectRRect;
357         if (fHeadInstance->fRRect.isRect() && clipRRect.isRect()) {
358             SkRect isectRect;
359             if (!isectRect.intersect(fHeadInstance->fRRect.rect(), clipRRect.rect())) {
360                 return ClipResult::kClippedOut;
361             }
362             isectRRect.setRect(isectRect);
363         } else {
364             isectRRect = SkRRectPriv::ConservativeIntersect(fHeadInstance->fRRect, clipRRect);
365             if (isectRRect.isEmpty()) {
366                 // The round rects did not intersect at all or the intersection was too complicated
367                 // to compute quickly.
368                 return ClipResult::kFail;
369             }
370         }
371 
372         // Don't apply the clip geometrically if it becomes subpixel, since then the hairline
373         // rendering may outset beyond the original clip.
374         SkRect devISectBounds = fHeadInstance->fViewMatrix.mapRect(isectRRect.rect());
375         if (devISectBounds.width() < 1.f || devISectBounds.height() < 1.f) {
376             return ClipResult::kFail;
377         }
378 
379         if (fHeadInstance->fLocalCoords.fType == LocalCoords::Type::kRect) {
380             // Update the local rect.
381             auto rect = sk_bit_cast<skvx::float4>(fHeadInstance->fRRect.rect());
382             auto local = sk_bit_cast<skvx::float4>(fHeadInstance->fLocalCoords.fRect);
383             auto isect = sk_bit_cast<skvx::float4>(isectRRect.rect());
384             auto rectToLocalSize = (local - skvx::shuffle<2,3,0,1>(local)) /
385                                    (rect - skvx::shuffle<2,3,0,1>(rect));
386             auto localCoordsRect = (isect - rect) * rectToLocalSize + local;
387             fHeadInstance->fLocalCoords.fRect.setLTRB(localCoordsRect.x(),
388                                                       localCoordsRect.y(),
389                                                       localCoordsRect.z(),
390                                                       localCoordsRect.w());
391         }
392 
393         // Update the round rect.
394         fHeadInstance->fRRect = isectRRect;
395         return ClipResult::kClippedGeometrically;
396     }
397 
398     return ClipResult::kFail;
399 }
400 
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)401 GrProcessorSet::Analysis FillRRectOpImpl::finalize(const GrCaps& caps, const GrAppliedClip* clip,
402                                                    GrClampType clampType) {
403     SkASSERT(fInstanceCount == 1);
404     SkASSERT(fHeadInstance->fNext == nullptr);
405 
406     bool isWideColor;
407     auto analysis = fHelper.finalizeProcessors(caps, clip, clampType,
408                                                GrProcessorAnalysisCoverage::kSingleChannel,
409                                                &fHeadInstance->fColor, &isWideColor);
410     if (isWideColor) {
411         fProcessorFlags |= ProcessorFlags::kWideColor;
412     }
413     if (analysis.usesLocalCoords()) {
414         fProcessorFlags |= ProcessorFlags::kHasLocalCoords;
415     }
416     return analysis;
417 }
418 
onCombineIfPossible(GrOp * op,SkArenaAlloc *,const GrCaps & caps)419 GrOp::CombineResult FillRRectOpImpl::onCombineIfPossible(GrOp* op,
420                                                          SkArenaAlloc*,
421                                                          const GrCaps& caps) {
422     auto that = op->cast<FillRRectOpImpl>();
423     if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds()) ||
424         fProcessorFlags != that->fProcessorFlags) {
425         return CombineResult::kCannotCombine;
426     }
427 
428     *fTailInstance = that->fHeadInstance;
429     fTailInstance = that->fTailInstance;
430     fInstanceCount += that->fInstanceCount;
431     return CombineResult::kMerged;
432 }
433 
434 #if defined(GPU_TEST_UTILS)
onDumpInfo() const435 SkString FillRRectOpImpl::onDumpInfo() const {
436     SkString str = SkStringPrintf("# instances: %d\n", fInstanceCount);
437     str += fHelper.dumpInfo();
438     int i = 0;
439     for (Instance* tmp = fHeadInstance; tmp; tmp = tmp->fNext, ++i) {
440         str.appendf("%d: Color: [%.2f, %.2f, %.2f, %.2f] ",
441                     i, tmp->fColor.fR, tmp->fColor.fG, tmp->fColor.fB, tmp->fColor.fA);
442         SkMatrix m = tmp->fViewMatrix;
443         str.appendf("ViewMatrix: [%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f] ",
444                     m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
445         SkRect r = tmp->fRRect.rect();
446         str.appendf("Rect: [%f %f %f %f]\n", r.fLeft, r.fTop, r.fRight, r.fBottom);
447     }
448     return str;
449 }
450 #endif
451 
452 class FillRRectOpImpl::Processor final : public GrGeometryProcessor {
453 public:
Make(SkArenaAlloc * arena,GrAAType aaType,ProcessorFlags flags)454     static GrGeometryProcessor* Make(SkArenaAlloc* arena, GrAAType aaType, ProcessorFlags flags) {
455         return arena->make([&](void* ptr) {
456             return new (ptr) Processor(aaType, flags);
457         });
458     }
459 
name() const460     const char* name() const override { return "FillRRectOp::Processor"; }
461 
addToKey(const GrShaderCaps & caps,KeyBuilder * b) const462     void addToKey(const GrShaderCaps& caps, KeyBuilder* b) const override {
463         b->addBits(kNumProcessorFlags, (uint32_t)fFlags,  "flags");
464     }
465 
466     std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override;
467 
468 private:
469     class Impl;
470 
Processor(GrAAType aaType,ProcessorFlags flags)471     Processor(GrAAType aaType, ProcessorFlags flags)
472             : GrGeometryProcessor(kGrFillRRectOp_Processor_ClassID)
473             , fFlags(flags) {
474         this->setVertexAttributesWithImplicitOffsets(kVertexAttribs, std::size(kVertexAttribs));
475 
476         fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, SkSLType::kFloat4);
477         fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, SkSLType::kFloat4);
478         fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, SkSLType::kFloat4);
479         if (fFlags & ProcessorFlags::kHasLocalCoords) {
480             fInstanceAttribs.emplace_back("translate_and_localrotate",
481                                           kFloat4_GrVertexAttribType,
482                                           SkSLType::kFloat4);
483             fInstanceAttribs.emplace_back(
484                     "localrect", kFloat4_GrVertexAttribType, SkSLType::kFloat4);
485         } else {
486             fInstanceAttribs.emplace_back("translate_and_localrotate",
487                                           kFloat2_GrVertexAttribType,
488                                           SkSLType::kFloat2);
489         }
490         fColorAttrib = &fInstanceAttribs.push_back(
491                 MakeColorAttribute("color", (fFlags & ProcessorFlags::kWideColor)));
492         SkASSERT(fInstanceAttribs.size() <= kMaxInstanceAttribs);
493         this->setInstanceAttributesWithImplicitOffsets(fInstanceAttribs.begin(),
494                                                        fInstanceAttribs.size());
495     }
496 
497     inline static constexpr Attribute kVertexAttribs[] = {
498             {"radii_selector", kFloat4_GrVertexAttribType, SkSLType::kFloat4},
499             {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, SkSLType::kFloat4},
500             // Coverage only.
501             {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, SkSLType::kFloat4}};
502 
503     const ProcessorFlags fFlags;
504 
505     constexpr static int kMaxInstanceAttribs = 6;
506     STArray<kMaxInstanceAttribs, Attribute> fInstanceAttribs;
507     const Attribute* fColorAttrib;
508 };
509 
510 // Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
511 // coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
512 // edges. The Vertex struct tells the shader where to place its vertex within a normalized
513 // ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
514 struct CoverageVertex {
515     std::array<float, 4> fRadiiSelector;
516     std::array<float, 2> fCorner;
517     std::array<float, 2> fRadiusOutset;
518     std::array<float, 2> fAABloatDirection;
519     float fCoverage;
520     float fIsLinearCoverage;
521 };
522 
523 // This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
524 // of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
525 // rectangles.
526 static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
527 
528 static constexpr CoverageVertex kVertexData[] = {
529         // Left inset edge.
530         {{{0,0,0,1}},  {{-1,+1}},  {{0,-1}},  {{+1,0}},  1,  1},
531         {{{1,0,0,0}},  {{-1,-1}},  {{0,+1}},  {{+1,0}},  1,  1},
532 
533         // Top inset edge.
534         {{{1,0,0,0}},  {{-1,-1}},  {{+1,0}},  {{0,+1}},  1,  1},
535         {{{0,1,0,0}},  {{+1,-1}},  {{-1,0}},  {{0,+1}},  1,  1},
536 
537         // Right inset edge.
538         {{{0,1,0,0}},  {{+1,-1}},  {{0,+1}},  {{-1,0}},  1,  1},
539         {{{0,0,1,0}},  {{+1,+1}},  {{0,-1}},  {{-1,0}},  1,  1},
540 
541         // Bottom inset edge.
542         {{{0,0,1,0}},  {{+1,+1}},  {{-1,0}},  {{0,-1}},  1,  1},
543         {{{0,0,0,1}},  {{-1,+1}},  {{+1,0}},  {{0,-1}},  1,  1},
544 
545 
546         // Left outset edge.
547         {{{0,0,0,1}},  {{-1,+1}},  {{0,-1}},  {{-1,0}},  0,  1},
548         {{{1,0,0,0}},  {{-1,-1}},  {{0,+1}},  {{-1,0}},  0,  1},
549 
550         // Top outset edge.
551         {{{1,0,0,0}},  {{-1,-1}},  {{+1,0}},  {{0,-1}},  0,  1},
552         {{{0,1,0,0}},  {{+1,-1}},  {{-1,0}},  {{0,-1}},  0,  1},
553 
554         // Right outset edge.
555         {{{0,1,0,0}},  {{+1,-1}},  {{0,+1}},  {{+1,0}},  0,  1},
556         {{{0,0,1,0}},  {{+1,+1}},  {{0,-1}},  {{+1,0}},  0,  1},
557 
558         // Bottom outset edge.
559         {{{0,0,1,0}},  {{+1,+1}},  {{-1,0}},  {{0,+1}},  0,  1},
560         {{{0,0,0,1}},  {{-1,+1}},  {{+1,0}},  {{0,+1}},  0,  1},
561 
562 
563         // Top-left corner.
564         {{{1,0,0,0}},  {{-1,-1}},  {{ 0,+1}},  {{-1, 0}},  0,  0},
565         {{{1,0,0,0}},  {{-1,-1}},  {{ 0,+1}},  {{+1, 0}},  1,  0},
566         {{{1,0,0,0}},  {{-1,-1}},  {{+1, 0}},  {{ 0,+1}},  1,  0},
567         {{{1,0,0,0}},  {{-1,-1}},  {{+1, 0}},  {{ 0,-1}},  0,  0},
568         {{{1,0,0,0}},  {{-1,-1}},  {{+kOctoOffset,0}},  {{-1,-1}},  0,  0},
569         {{{1,0,0,0}},  {{-1,-1}},  {{0,+kOctoOffset}},  {{-1,-1}},  0,  0},
570 
571         // Top-right corner.
572         {{{0,1,0,0}},  {{+1,-1}},  {{-1, 0}},  {{ 0,-1}},  0,  0},
573         {{{0,1,0,0}},  {{+1,-1}},  {{-1, 0}},  {{ 0,+1}},  1,  0},
574         {{{0,1,0,0}},  {{+1,-1}},  {{ 0,+1}},  {{-1, 0}},  1,  0},
575         {{{0,1,0,0}},  {{+1,-1}},  {{ 0,+1}},  {{+1, 0}},  0,  0},
576         {{{0,1,0,0}},  {{+1,-1}},  {{0,+kOctoOffset}},  {{+1,-1}},  0,  0},
577         {{{0,1,0,0}},  {{+1,-1}},  {{-kOctoOffset,0}},  {{+1,-1}},  0,  0},
578 
579         // Bottom-right corner.
580         {{{0,0,1,0}},  {{+1,+1}},  {{ 0,-1}},  {{+1, 0}},  0,  0},
581         {{{0,0,1,0}},  {{+1,+1}},  {{ 0,-1}},  {{-1, 0}},  1,  0},
582         {{{0,0,1,0}},  {{+1,+1}},  {{-1, 0}},  {{ 0,-1}},  1,  0},
583         {{{0,0,1,0}},  {{+1,+1}},  {{-1, 0}},  {{ 0,+1}},  0,  0},
584         {{{0,0,1,0}},  {{+1,+1}},  {{-kOctoOffset,0}},  {{+1,+1}},  0,  0},
585         {{{0,0,1,0}},  {{+1,+1}},  {{0,-kOctoOffset}},  {{+1,+1}},  0,  0},
586 
587         // Bottom-left corner.
588         {{{0,0,0,1}},  {{-1,+1}},  {{+1, 0}},  {{ 0,+1}},  0,  0},
589         {{{0,0,0,1}},  {{-1,+1}},  {{+1, 0}},  {{ 0,-1}},  1,  0},
590         {{{0,0,0,1}},  {{-1,+1}},  {{ 0,-1}},  {{+1, 0}},  1,  0},
591         {{{0,0,0,1}},  {{-1,+1}},  {{ 0,-1}},  {{-1, 0}},  0,  0},
592         {{{0,0,0,1}},  {{-1,+1}},  {{0,-kOctoOffset}},  {{-1,+1}},  0,  0},
593         {{{0,0,0,1}},  {{-1,+1}},  {{+kOctoOffset,0}},  {{-1,+1}},  0,  0}};
594 
595 SKGPU_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);
596 
597 static constexpr uint16_t kIndexData[] = {
598         // Inset octagon (solid coverage).
599         0, 1, 7,
600         1, 2, 7,
601         7, 2, 6,
602         2, 3, 6,
603         6, 3, 5,
604         3, 4, 5,
605 
606         // AA borders (linear coverage).
607         0, 1, 8, 1, 9, 8,
608         2, 3, 10, 3, 11, 10,
609         4, 5, 12, 5, 13, 12,
610         6, 7, 14, 7, 15, 14,
611 
612         // Top-left arc.
613         16, 17, 21,
614         17, 21, 18,
615         21, 18, 20,
616         18, 20, 19,
617 
618         // Top-right arc.
619         22, 23, 27,
620         23, 27, 24,
621         27, 24, 26,
622         24, 26, 25,
623 
624         // Bottom-right arc.
625         28, 29, 33,
626         29, 33, 30,
627         33, 30, 32,
628         30, 32, 31,
629 
630         // Bottom-left arc.
631         34, 35, 39,
632         35, 39, 36,
633         39, 36, 38,
634         36, 38, 37};
635 
636 SKGPU_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
637 
onPrepareDraws(GrMeshDrawTarget * target)638 void FillRRectOpImpl::onPrepareDraws(GrMeshDrawTarget* target) {
639     if (!fProgramInfo) {
640         this->createProgramInfo(target);
641     }
642 
643     size_t instanceStride = fProgramInfo->geomProc().instanceStride();
644 
645     if (VertexWriter instanceWriter = target->makeVertexWriter(instanceStride, fInstanceCount,
646                                                                &fInstanceBuffer, &fBaseInstance)) {
647         SkDEBUGCODE(auto end = instanceWriter.mark(instanceStride * fInstanceCount));
648         for (Instance* i = fHeadInstance; i; i = i->fNext) {
649             auto [l, t, r, b] = i->fRRect.rect();
650 
651             // Produce a matrix that draws the round rect from normalized [-1, -1, +1, +1] space.
652             SkMatrix m;
653             // Unmap the normalized rect [-1, -1, +1, +1] back to [l, t, r, b].
654             m.setScaleTranslate((r - l)/2, (b - t)/2, (l + r)/2, (t + b)/2);
655             // Map to device space.
656             m.postConcat(i->fViewMatrix);
657 
658             // Convert the radii to [-1, -1, +1, +1] space and write their attribs.
659             skvx::float4 radiiX, radiiY;
660             skvx::strided_load2(&SkRRectPriv::GetRadiiArray(i->fRRect)->fX, radiiX, radiiY);
661             radiiX *= 2 / (r - l);
662             radiiY *= 2 / (b - t);
663 
664             instanceWriter << radiiX << radiiY
665                            << m.getScaleX() << m.getSkewX() << m.getSkewY() << m.getScaleY()
666                            << m.getTranslateX() << m.getTranslateY();
667 
668             if (fProcessorFlags & ProcessorFlags::kHasLocalCoords) {
669                 if (i->fLocalCoords.fType == LocalCoords::Type::kRect) {
670                     instanceWriter << 0.f << 0.f  // localrotate
671                                    << i->fLocalCoords.fRect;  // localrect
672                 } else {
673                     SkASSERT(i->fLocalCoords.fType == LocalCoords::Type::kMatrix);
674                     const SkRect& bounds = i->fRRect.rect();
675                     const SkMatrix& localMatrix = i->fLocalCoords.fMatrix;
676                     SkVector u = localMatrix.mapVector(bounds.right() - bounds.left(), 0);
677                     SkVector v = localMatrix.mapVector(0, bounds.bottom() - bounds.top());
678                     SkPoint l0 = localMatrix.mapPoint({bounds.left(), bounds.top()});
679                     instanceWriter << v.x() << u.y()  // localrotate
680                                    << l0 << (l0.x() + u.x()) << (l0.y() + v.y());  // localrect
681                 }
682             }
683 
684             instanceWriter << VertexColor(i->fColor, fProcessorFlags & ProcessorFlags::kWideColor);
685         }
686         SkASSERT(instanceWriter.mark() == end);
687     }
688 
689     SKGPU_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
690 
691     fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kIndex,
692                                                                       sizeof(kIndexData),
693                                                                       kIndexData, gIndexBufferKey);
694 
695     SKGPU_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
696 
697     fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,
698                                                                       sizeof(kVertexData),
699                                                                       kVertexData,
700                                                                       gVertexBufferKey);
701 }
702 
703 class FillRRectOpImpl::Processor::Impl : public ProgramImpl {
704 public:
setData(const GrGLSLProgramDataManager &,const GrShaderCaps &,const GrGeometryProcessor &)705     void setData(const GrGLSLProgramDataManager&,
706                  const GrShaderCaps&,
707                  const GrGeometryProcessor&) override {}
708 
709 private:
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)710     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
711         GrGLSLVertexBuilder* v = args.fVertBuilder;
712         GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
713 
714         const auto& proc = args.fGeomProc.cast<Processor>();
715         bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
716 
717         SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
718 
719         GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
720         varyings->emitAttributes(proc);
721         f->codeAppendf("half4 %s;", args.fOutputColor);
722         varyings->addPassThroughAttribute(proc.fColorAttrib->asShaderVar(),
723                                           args.fOutputColor,
724                                           GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
725 
726         // Emit the vertex shader.
727         // When MSAA is enabled, we need to make sure every sample gets lit up on pixels that have
728         // fractional coverage. We do this by making the ramp wider.
729         v->codeAppendf("float aa_bloat_multiplier = %i;",
730                        (proc.fFlags & ProcessorFlags::kMSAAEnabled)
731                                ? 2    // Outset an entire pixel (2 radii).
732                        : (!(proc.fFlags & ProcessorFlags::kFakeNonAA))
733                                ? 1    // Outset one half pixel (1 radius).
734                                : 0);  // No AA bloat.
735 
736         // Unpack vertex attribs.
737         v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
738         v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
739         v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
740         v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
741 
742         // Find the amount to bloat each edge for AA (in source space).
743         v->codeAppend("float2 pixellength = inversesqrt("
744                               "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
745         v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
746         v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
747                                            "abs(normalized_axis_dirs.zw));");
748         v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
749 
750         // Identify our radii.
751         v->codeAppend("float4 radii_and_neighbors = radii_selector"
752                               "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
753         v->codeAppend("float2 radii = radii_and_neighbors.xy;");
754         v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
755 
756         v->codeAppend("float coverage_multiplier = 1;");
757         v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
758                           // The rrect is more narrow than a half-pixel AA coverage ramp. We can't
759                           // draw as-is or else opposite AA borders will overlap. Instead, fudge the
760                           // size up to the width of a coverage ramp, and then reduce total coverage
761                           // to make the rect appear more thin.
762         v->codeAppend(    "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
763         v->codeAppend(    "coverage_multiplier = 1 / (max(aa_bloatradius.x, 1) * "
764                                                      "max(aa_bloatradius.y, 1));");
765                           // Set radii to zero to ensure we take the "linear coverage" codepath.
766                           // (The "coverage" variable only has effect in the linear codepath.)
767         v->codeAppend(    "radii = float2(0);");
768         v->codeAppend("}");
769 
770         // Unpack coverage.
771         v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
772         if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
773             // MSAA has a wider ramp that goes from -.5 to 1.5 instead of 0 to 1.
774             v->codeAppendf("coverage = (coverage - .5) * aa_bloat_multiplier + .5;");
775         }
776 
777         v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.5))) {");
778                           // The radii are very small. Demote this arc to a sharp 90 degree corner.
779         v->codeAppend(    "radii = float2(0);");
780                           // Convert to a standard picture frame for an AA rect instead of the round
781                           // rect geometry.
782         v->codeAppend(    "aa_bloat_direction = sign(corner);");
783         v->codeAppend(    "if (coverage > .5) {");  // Are we an inset edge?
784         v->codeAppend(        "aa_bloat_direction = -aa_bloat_direction;");
785         v->codeAppend(    "}");
786         v->codeAppend(    "is_linear_coverage = 1;");
787         v->codeAppend("} else {");
788                           // Don't let radii get smaller than a coverage ramp plus an extra half
789                           // pixel for MSAA. Always use the same amount so we don't pop when
790                           // switching between MSAA and coverage.
791         v->codeAppend(    "radii = clamp(radii, pixellength * 1.5, 2 - pixellength * 1.5);");
792         v->codeAppend(    "neighbor_radii = clamp(neighbor_radii, pixellength * 1.5, "
793                                                  "2 - pixellength * 1.5);");
794                           // Don't let neighboring radii get closer together than 1/16 pixel.
795         v->codeAppend(    "float2 spacing = 2 - radii - neighbor_radii;");
796         v->codeAppend(    "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
797         v->codeAppend(    "radii -= extra_pad * .5;");
798         v->codeAppend("}");
799 
800         // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
801         // normalized [-1,-1,+1,+1] space.
802         v->codeAppend("float2 aa_outset = "
803                               "aa_bloat_direction * aa_bloatradius * aa_bloat_multiplier;");
804         v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
805 
806         v->codeAppend("if (coverage > .5) {");  // Are we an inset edge?
807                           // Don't allow the aa insets to overlap. i.e., Don't let them inset past
808                           // the center (x=y=0). Since we don't allow the rect to become thinner
809                           // than 1px, this should only happen when using MSAA, where we inset by an
810                           // entire pixel instead of half.
811         v->codeAppend(    "if (aa_bloat_direction.x != 0 && vertexpos.x * corner.x < 0) {");
812         v->codeAppend(        "float backset = abs(vertexpos.x);");
813         v->codeAppend(        "vertexpos.x = 0;");
814         v->codeAppend(        "vertexpos.y += "
815                                       "backset * sign(corner.y) * pixellength.y/pixellength.x;");
816         v->codeAppend(        "coverage = (coverage - .5) * abs(corner.x) / "
817                                       "(abs(corner.x) + backset) + .5;");
818         v->codeAppend(    "}");
819         v->codeAppend(    "if (aa_bloat_direction.y != 0 && vertexpos.y * corner.y < 0) {");
820         v->codeAppend(        "float backset = abs(vertexpos.y);");
821         v->codeAppend(        "vertexpos.y = 0;");
822         v->codeAppend(        "vertexpos.x += "
823                                       "backset * sign(corner.x) * pixellength.x/pixellength.y;");
824         v->codeAppend(        "coverage = (coverage - .5) * abs(corner.y) / "
825                                       "(abs(corner.y) + backset) + .5;");
826         v->codeAppend(    "}");
827         v->codeAppend("}");
828 
829         // Transform to device space.
830         v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
831         v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate_and_localrotate.xy;");
832         gpArgs->fPositionVar.set(SkSLType::kFloat2, "devcoord");
833 
834         // Output local coordinates.
835         if (proc.fFlags & ProcessorFlags::kHasLocalCoords) {
836             // Do math in a way that preserves exact local coord boundaries when there is no local
837             // rotate and vertexpos is on an exact shape boundary.
838             v->codeAppend("float2 T = vertexpos * .5 + .5;");
839             v->codeAppend("float2 localcoord = localrect.xy * (1 - T) + "
840                                                "localrect.zw * T + "
841                                                "translate_and_localrotate.zw * T.yx;");
842             gpArgs->fLocalCoordVar.set(SkSLType::kFloat2, "localcoord");
843         }
844 
845         // Setup interpolants for coverage.
846         GrGLSLVarying arcCoord(useHWDerivatives ? SkSLType::kFloat2 : SkSLType::kFloat4);
847         varyings->addVarying("arccoord", &arcCoord);
848         v->codeAppend("if (0 != is_linear_coverage) {");
849                            // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
850                            // interpolate linear coverage across y.
851         v->codeAppendf(    "%s.xy = float2(0, coverage * coverage_multiplier);",
852                            arcCoord.vsOut());
853         v->codeAppend("} else {");
854                            // Find the normalized arc coordinates for our corner ellipse.
855                            // (i.e., the coordinate system where x^2 + y^2 == 1).
856         v->codeAppend(    "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
857                            // We are a corner piece: Interpolate the arc coordinates for coverage.
858                            // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
859                            // instructs the fragment shader to use linear coverage).
860         v->codeAppendf(    "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
861         if (!useHWDerivatives) {
862             // The gradient is order-1: Interpolate it across arccoord.zw.
863             v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
864             v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
865         }
866         v->codeAppend("}");
867 
868         // Emit the fragment shader.
869         f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
870         f->codeAppendf("half coverage;");
871         f->codeAppendf("if (0 == x_plus_1) {");
872         f->codeAppendf(    "coverage = half(y);");  // We are a non-arc pixel (linear coverage).
873         f->codeAppendf("} else {");
874         f->codeAppendf(    "float fn = x_plus_1 * (x_plus_1 - 2);");  // fn = (x+1)*(x-1) = x^2-1
875         f->codeAppendf(    "fn = fma(y,y, fn);");  // fn = x^2 + y^2 - 1
876         if (useHWDerivatives) {
877             f->codeAppendf("float fnwidth = fwidth(fn);");
878         } else {
879             // The gradient is interpolated across arccoord.zw.
880             f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
881             f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
882         }
883         f->codeAppendf(    "coverage = .5 - half(fn/fnwidth);");
884         if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
885             // MSAA uses ramps larger than 1px, so we need to clamp in both branches.
886             f->codeAppendf("}");
887         }
888         f->codeAppendf("coverage = clamp(coverage, 0, 1);");
889         if (!(proc.fFlags & ProcessorFlags::kMSAAEnabled)) {
890             // When not using MSAA, we only need to clamp in the "arc" branch.
891             f->codeAppendf("}");
892         }
893         if (proc.fFlags & ProcessorFlags::kFakeNonAA) {
894             f->codeAppendf("coverage = (coverage >= .5) ? 1 : 0;");
895         }
896         f->codeAppendf("half4 %s = half4(coverage);", args.fOutputCoverage);
897     }
898 };
899 
makeProgramImpl(const GrShaderCaps &) const900 std::unique_ptr<GrGeometryProcessor::ProgramImpl> FillRRectOpImpl::Processor::makeProgramImpl(
901         const GrShaderCaps&) const {
902     return std::make_unique<Impl>();
903 }
904 
onCreateProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,bool usesMSAASurface,GrAppliedClip && appliedClip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)905 void FillRRectOpImpl::onCreateProgramInfo(const GrCaps* caps,
906                                           SkArenaAlloc* arena,
907                                           const GrSurfaceProxyView& writeView,
908                                           bool usesMSAASurface,
909                                           GrAppliedClip&& appliedClip,
910                                           const GrDstProxyView& dstProxyView,
911                                           GrXferBarrierFlags renderPassXferBarriers,
912                                           GrLoadOp colorLoadOp) {
913     if (usesMSAASurface) {
914         fProcessorFlags |= ProcessorFlags::kMSAAEnabled;
915     }
916     GrGeometryProcessor* gp = Processor::Make(arena, fHelper.aaType(), fProcessorFlags);
917     fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, usesMSAASurface,
918                                              std::move(appliedClip), dstProxyView, gp,
919                                              GrPrimitiveType::kTriangles, renderPassXferBarriers,
920                                              colorLoadOp);
921 }
922 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)923 void FillRRectOpImpl::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
924     if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
925         return;  // Setup failed.
926     }
927 
928     flushState->bindPipelineAndScissorClip(*fProgramInfo, this->bounds());
929     flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
930     flushState->bindBuffers(std::move(fIndexBuffer), std::move(fInstanceBuffer),
931                             std::move(fVertexBuffer));
932     flushState->drawIndexedInstanced(std::size(kIndexData), 0, fInstanceCount, fBaseInstance, 0);
933 }
934 
935 // Will the given corner look good if we use HW derivatives?
can_use_hw_derivatives_with_coverage(const skvx::float2 & devScale,const skvx::float2 & cornerRadii)936 bool can_use_hw_derivatives_with_coverage(const skvx::float2& devScale,
937                                           const skvx::float2& cornerRadii) {
938     skvx::float2 devRadii = devScale * cornerRadii;
939     if (devRadii[1] < devRadii[0]) {
940         devRadii = skvx::shuffle<1,0>(devRadii);
941     }
942     float minDevRadius = std::max(devRadii[0], 1.f);  // Shader clamps radius at a minimum of 1.
943     // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
944     // This threshold was arrived at subjevtively on an NVIDIA chip.
945     return minDevRadius * minDevRadius * 5 > devRadii[1];
946 }
947 
can_use_hw_derivatives_with_coverage(const skvx::float2 & devScale,const SkVector & cornerRadii)948 bool can_use_hw_derivatives_with_coverage(const skvx::float2& devScale,
949                                           const SkVector& cornerRadii) {
950     return can_use_hw_derivatives_with_coverage(devScale, skvx::float2::Load(&cornerRadii));
951 }
952 
953 // 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)954 bool can_use_hw_derivatives_with_coverage(const GrShaderCaps& shaderCaps,
955                                           const SkMatrix& viewMatrix,
956                                           const SkRRect& rrect) {
957     if (!shaderCaps.fShaderDerivativeSupport) {
958         return false;
959     }
960 
961     auto x = skvx::float2(viewMatrix.getScaleX(), viewMatrix.getSkewX());
962     auto y = skvx::float2(viewMatrix.getSkewY(), viewMatrix.getScaleY());
963     skvx::float2 devScale = sqrt(x*x + y*y);
964     switch (rrect.getType()) {
965         case SkRRect::kEmpty_Type:
966         case SkRRect::kRect_Type:
967             return true;
968 
969         case SkRRect::kOval_Type:
970         case SkRRect::kSimple_Type:
971             return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
972 
973         case SkRRect::kNinePatch_Type: {
974             skvx::float2 r0 = skvx::float2::Load(SkRRectPriv::GetRadiiArray(rrect));
975             skvx::float2 r1 = skvx::float2::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
976             skvx::float2 minRadii = min(r0, r1);
977             skvx::float2 maxRadii = max(r0, r1);
978             return can_use_hw_derivatives_with_coverage(devScale,
979                                                         skvx::float2(minRadii[0], maxRadii[1])) &&
980                    can_use_hw_derivatives_with_coverage(devScale,
981                                                         skvx::float2(maxRadii[0], minRadii[1]));
982         }
983 
984         case SkRRect::kComplex_Type: {
985             for (int i = 0; i < 4; ++i) {
986                 auto corner = static_cast<SkRRect::Corner>(i);
987                 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
988                     return false;
989                 }
990             }
991             return true;
992         }
993     }
994     SK_ABORT("Invalid round rect type.");
995 }
996 
997 } // anonymous namespace
998 
Make(GrRecordingContext * ctx,SkArenaAlloc * arena,GrPaint && paint,const SkMatrix & viewMatrix,const SkRRect & rrect,const SkRect & localRect,GrAA aa)999 GrOp::Owner Make(GrRecordingContext* ctx,
1000                  SkArenaAlloc* arena,
1001                  GrPaint&& paint,
1002                  const SkMatrix& viewMatrix,
1003                  const SkRRect& rrect,
1004                  const SkRect& localRect,
1005                  GrAA aa) {
1006     return FillRRectOpImpl::Make(ctx, arena, std::move(paint), viewMatrix, rrect, localRect, aa);
1007 }
1008 
Make(GrRecordingContext * ctx,SkArenaAlloc * arena,GrPaint && paint,const SkMatrix & viewMatrix,const SkRRect & rrect,const SkMatrix & localMatrix,GrAA aa)1009 GrOp::Owner Make(GrRecordingContext* ctx,
1010                  SkArenaAlloc* arena,
1011                  GrPaint&& paint,
1012                  const SkMatrix& viewMatrix,
1013                  const SkRRect& rrect,
1014                  const SkMatrix& localMatrix,
1015                  GrAA aa) {
1016     return FillRRectOpImpl::Make(ctx, arena, std::move(paint), viewMatrix, rrect, localMatrix, aa);
1017 }
1018 
1019 }  // namespace skgpu::ganesh::FillRRectOp
1020 
1021 #if defined(GPU_TEST_UTILS)
1022 
GR_DRAW_OP_TEST_DEFINE(FillRRectOp)1023 GR_DRAW_OP_TEST_DEFINE(FillRRectOp) {
1024     SkArenaAlloc arena(64 * sizeof(float));
1025     SkMatrix viewMatrix = GrTest::TestMatrix(random);
1026     GrAA aa = GrAA(random->nextBool());
1027 
1028     SkRect rect = GrTest::TestRect(random);
1029     float w = rect.width();
1030     float h = rect.height();
1031 
1032     SkRRect rrect;
1033     // TODO: test out other rrect configurations
1034     rrect.setNinePatch(rect, w / 3.0f, h / 4.0f, w / 5.0f, h / 6.0);
1035 
1036     return skgpu::ganesh::FillRRectOp::Make(
1037             context, &arena, std::move(paint), viewMatrix, rrect, rrect.rect(), aa);
1038 }
1039 
1040 #endif
1041