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