blob: 10c9c2a3049afbb6b5ed56cec4f3860df83479c2 [file] [log] [blame]
Chris Dalton133944a2018-11-16 23:30:29 -05001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/gpu/ops/GrFillRRectOp.h"
Chris Dalton133944a2018-11-16 23:30:29 -05009
Robert Phillipsb7bfbc22020-07-01 12:55:01 -040010#include "include/gpu/GrRecordingContext.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "src/core/SkRRectPriv.h"
12#include "src/gpu/GrCaps.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "src/gpu/GrMemoryPool.h"
14#include "src/gpu/GrOpFlushState.h"
Greg Daniel2d41d0d2019-08-26 11:08:51 -040015#include "src/gpu/GrOpsRenderPass.h"
Robert Phillips901aff02019-10-08 12:32:56 -040016#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/gpu/GrRecordingContextPriv.h"
Robert Phillips1a82a4e2021-07-01 10:27:44 -040018#include "src/gpu/GrResourceProvider.h"
Chris Dalton3c636f02021-07-08 14:50:57 -060019#include "src/gpu/GrVertexWriter.h"
20#include "src/gpu/GrVx.h"
Chris Daltonea46ef32021-07-12 15:09:06 -060021#include "src/gpu/geometry/GrShape.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050022#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
23#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
24#include "src/gpu/glsl/GrGLSLVarying.h"
25#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
Robert Phillipscad8fba2020-03-20 15:39:29 -040026#include "src/gpu/ops/GrMeshDrawOp.h"
Robert Phillipsce978572020-02-28 11:56:44 -050027#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
Robert Phillips366176b2020-02-26 11:40:50 -050028
29namespace {
30
Robert Phillipscad8fba2020-03-20 15:39:29 -040031class FillRRectOp : public GrMeshDrawOp {
Robert Phillips360ec182020-03-26 13:29:50 -040032private:
33 using Helper = GrSimpleMeshDrawOpHelper;
34
Robert Phillips366176b2020-02-26 11:40:50 -050035public:
36 DEFINE_OP_CLASS_ID
37
Herb Derbyc76d4092020-10-07 16:46:15 -040038 static GrOp::Owner Make(GrRecordingContext*,
Chris Dalton3c636f02021-07-08 14:50:57 -060039 SkArenaAlloc*,
Herb Derbyc76d4092020-10-07 16:46:15 -040040 GrPaint&&,
41 const SkMatrix& viewMatrix,
42 const SkRRect&,
Chris Dalton61d694d2021-03-22 00:00:52 -060043 const SkRect& localRect,
Chris Dalton4f447342021-03-12 12:09:12 -070044 GrAA);
Robert Phillips366176b2020-02-26 11:40:50 -050045
46 const char* name() const final { return "GrFillRRectOp"; }
47
Robert Phillips360ec182020-03-26 13:29:50 -040048 FixedFunctionFlags fixedFunctionFlags() const final { return fHelper.fixedFunctionFlags(); }
49
Chris Daltonea46ef32021-07-12 15:09:06 -060050 ClipResult clipToShape(GrSurfaceDrawContext*, SkClipOp, const SkMatrix& clipMatrix,
51 const GrShape&, GrAA) override;
52
Chris Dalton57ab06c2021-04-22 12:57:28 -060053 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) final;
Herb Derbye25c3002020-10-27 15:57:27 -040054 CombineResult onCombineIfPossible(GrOp*, SkArenaAlloc*, const GrCaps&) final;
Robert Phillips360ec182020-03-26 13:29:50 -040055
Robert Phillips294723d2021-06-17 09:23:58 -040056 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips366176b2020-02-26 11:40:50 -050057 if (fProgramInfo) {
Robert Phillips294723d2021-06-17 09:23:58 -040058 fProgramInfo->visitFPProxies(func);
Robert Phillips366176b2020-02-26 11:40:50 -050059 } else {
Robert Phillips294723d2021-06-17 09:23:58 -040060 fHelper.visitProxies(func);
Robert Phillips366176b2020-02-26 11:40:50 -050061 }
62 }
63
Robert Phillips71143952021-06-17 14:55:07 -040064 void onPrepareDraws(GrMeshDrawTarget*) final;
Robert Phillips366176b2020-02-26 11:40:50 -050065
66 void onExecute(GrOpFlushState*, const SkRect& chainBounds) final;
67
68private:
Robert Phillips360ec182020-03-26 13:29:50 -040069 friend class ::GrSimpleMeshDrawOpHelper; // for access to ctor
Herb Derbyc76d4092020-10-07 16:46:15 -040070 friend class ::GrOp; // for access to ctor
Robert Phillips360ec182020-03-26 13:29:50 -040071
72 enum class ProcessorFlags {
Robert Phillips366176b2020-02-26 11:40:50 -050073 kNone = 0,
74 kUseHWDerivatives = 1 << 0,
Chris Daltoncc13b352021-03-05 14:59:01 -070075 kHasLocalCoords = 1 << 1,
76 kWideColor = 1 << 2,
77 kMSAAEnabled = 1 << 3,
78 kFakeNonAA = 1 << 4,
Robert Phillips366176b2020-02-26 11:40:50 -050079 };
Chris Daltoncc13b352021-03-05 14:59:01 -070080 constexpr static int kNumProcessorFlags = 5;
Robert Phillips366176b2020-02-26 11:40:50 -050081
Robert Phillips360ec182020-03-26 13:29:50 -040082 GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(ProcessorFlags);
Robert Phillips366176b2020-02-26 11:40:50 -050083
84 class Processor;
85
Herb Derbyc76d4092020-10-07 16:46:15 -040086 FillRRectOp(GrProcessorSet*,
Robert Phillips360ec182020-03-26 13:29:50 -040087 const SkPMColor4f& paintColor,
Chris Dalton3c636f02021-07-08 14:50:57 -060088 SkArenaAlloc*,
89 const SkMatrix& viewMatrix,
Robert Phillips360ec182020-03-26 13:29:50 -040090 const SkRRect&,
Chris Dalton61d694d2021-03-22 00:00:52 -060091 const SkRect& localRect,
Chris Dalton3c636f02021-07-08 14:50:57 -060092 ProcessorFlags);
Robert Phillips366176b2020-02-26 11:40:50 -050093
Robert Phillipscad8fba2020-03-20 15:39:29 -040094 GrProgramInfo* programInfo() final { return fProgramInfo; }
95
Robert Phillips366176b2020-02-26 11:40:50 -050096 // Create a GrProgramInfo object in the provided arena
Robert Phillipscad8fba2020-03-20 15:39:29 -040097 void onCreateProgramInfo(const GrCaps*,
98 SkArenaAlloc*,
Adlai Hollere2296f72020-11-19 13:41:26 -050099 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400100 GrAppliedClip&&,
John Stiles52cb1d02021-06-02 11:58:05 -0400101 const GrDstProxyView&,
Greg Daniel42dbca52020-11-20 10:22:43 -0500102 GrXferBarrierFlags renderPassXferBarriers,
103 GrLoadOp colorLoadOp) final;
Robert Phillips366176b2020-02-26 11:40:50 -0500104
Robert Phillips360ec182020-03-26 13:29:50 -0400105 Helper fHelper;
Robert Phillips360ec182020-03-26 13:29:50 -0400106 ProcessorFlags fProcessorFlags;
Robert Phillips366176b2020-02-26 11:40:50 -0500107
Chris Dalton3c636f02021-07-08 14:50:57 -0600108 struct Instance {
109 Instance(const SkMatrix& viewMatrix, const SkRRect& rrect, const SkRect& localRect,
110 const SkPMColor4f& color)
111 : fViewMatrix(viewMatrix), fRRect(rrect), fLocalRect(localRect), fColor(color) {}
112 SkMatrix fViewMatrix;
113 SkRRect fRRect;
114 SkRect fLocalRect;
115 SkPMColor4f fColor;
116 Instance* fNext = nullptr;
117 };
118
119 Instance* fHeadInstance;
120 Instance** fTailInstance;
Robert Phillips366176b2020-02-26 11:40:50 -0500121 int fInstanceCount = 1;
Robert Phillips366176b2020-02-26 11:40:50 -0500122
123 sk_sp<const GrBuffer> fInstanceBuffer;
124 sk_sp<const GrBuffer> fVertexBuffer;
125 sk_sp<const GrBuffer> fIndexBuffer;
126 int fBaseInstance = 0;
Robert Phillips366176b2020-02-26 11:40:50 -0500127
128 // If this op is prePrepared the created programInfo will be stored here for use in
129 // onExecute. In the prePrepared case it will have been stored in the record-time arena.
130 GrProgramInfo* fProgramInfo = nullptr;
131
John Stiles7571f9e2020-09-02 22:42:33 -0400132 using INHERITED = GrMeshDrawOp;
Robert Phillips366176b2020-02-26 11:40:50 -0500133};
134
Robert Phillips360ec182020-03-26 13:29:50 -0400135GR_MAKE_BITFIELD_CLASS_OPS(FillRRectOp::ProcessorFlags)
Chris Dalton133944a2018-11-16 23:30:29 -0500136
137// Hardware derivatives are not always accurate enough for highly elliptical corners. This method
138// checks to make sure the corners will still all look good if we use HW derivatives.
Robert Phillips360ec182020-03-26 13:29:50 -0400139static bool can_use_hw_derivatives_with_coverage(const GrShaderCaps&,
140 const SkMatrix&,
141 const SkRRect&);
Chris Dalton133944a2018-11-16 23:30:29 -0500142
Herb Derbyc76d4092020-10-07 16:46:15 -0400143GrOp::Owner FillRRectOp::Make(GrRecordingContext* ctx,
Chris Dalton3c636f02021-07-08 14:50:57 -0600144 SkArenaAlloc* arena,
Herb Derbyc76d4092020-10-07 16:46:15 -0400145 GrPaint&& paint,
146 const SkMatrix& viewMatrix,
147 const SkRRect& rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600148 const SkRect& localRect,
Chris Dalton4f447342021-03-12 12:09:12 -0700149 GrAA aa) {
Robert Phillips360ec182020-03-26 13:29:50 -0400150 using Helper = GrSimpleMeshDrawOpHelper;
151
152 const GrCaps* caps = ctx->priv().caps();
153
Chris Daltona77cdee2020-04-03 14:50:43 -0600154 if (!caps->drawInstancedSupport()) {
Chris Dalton133944a2018-11-16 23:30:29 -0500155 return nullptr;
156 }
157
Chris Dalton61d694d2021-03-22 00:00:52 -0600158 // We transform into a normalized -1..+1 space to draw the round rect. If the boundaries are too
159 // large, the math can overflow. The caller can fall back on path rendering if this is the case.
160 if (std::max(rrect.height(), rrect.width()) >= 1e6f) {
161 return nullptr;
162 }
163
Robert Phillips360ec182020-03-26 13:29:50 -0400164 ProcessorFlags flags = ProcessorFlags::kNone;
Chris Daltoncc13b352021-03-05 14:59:01 -0700165 // TODO: Support perspective in a follow-on CL. This shouldn't be difficult, since we already
166 // use HW derivatives. The only trick will be adjusting the AA outset to account for
167 // perspective. (i.e., outset = 0.5 * z.)
168 if (viewMatrix.hasPerspective()) {
169 return nullptr;
170 }
171 if (can_use_hw_derivatives_with_coverage(*caps->shaderCaps(), viewMatrix, rrect)) {
172 // HW derivatives (more specifically, fwidth()) are consistently faster on all platforms in
173 // coverage mode. We use them as long as the approximation will be accurate enough.
174 flags |= ProcessorFlags::kUseHWDerivatives;
175 }
Chris Dalton4f447342021-03-12 12:09:12 -0700176 if (aa == GrAA::kNo) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700177 flags |= ProcessorFlags::kFakeNonAA;
Chris Dalton133944a2018-11-16 23:30:29 -0500178 }
179
Chris Dalton3c636f02021-07-08 14:50:57 -0600180 return Helper::FactoryHelper<FillRRectOp>(ctx, std::move(paint), arena, viewMatrix, rrect,
181 localRect, flags);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600182}
183
Herb Derbyc76d4092020-10-07 16:46:15 -0400184FillRRectOp::FillRRectOp(GrProcessorSet* processorSet,
Robert Phillips360ec182020-03-26 13:29:50 -0400185 const SkPMColor4f& paintColor,
Chris Dalton3c636f02021-07-08 14:50:57 -0600186 SkArenaAlloc* arena,
187 const SkMatrix& viewMatrix,
Robert Phillips360ec182020-03-26 13:29:50 -0400188 const SkRRect& rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600189 const SkRect& localRect,
Chris Dalton3c636f02021-07-08 14:50:57 -0600190 ProcessorFlags processorFlags)
Robert Phillipscad8fba2020-03-20 15:39:29 -0400191 : INHERITED(ClassID())
Chris Daltoncc13b352021-03-05 14:59:01 -0700192 , fHelper(processorSet,
193 (processorFlags & ProcessorFlags::kFakeNonAA)
194 ? GrAAType::kNone
195 : GrAAType::kCoverage) // Use analytic AA even if the RT is MSAA.
Robert Phillips360ec182020-03-26 13:29:50 -0400196 , fProcessorFlags(processorFlags & ~(ProcessorFlags::kHasLocalCoords |
Chris Daltoncc13b352021-03-05 14:59:01 -0700197 ProcessorFlags::kWideColor |
Chris Dalton3c636f02021-07-08 14:50:57 -0600198 ProcessorFlags::kMSAAEnabled))
199 , fHeadInstance(arena->make<Instance>(viewMatrix, rrect, localRect, paintColor))
200 , fTailInstance(&fHeadInstance->fNext) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700201 // FillRRectOp::Make fails if there is perspective.
Chris Dalton3c636f02021-07-08 14:50:57 -0600202 SkASSERT(!viewMatrix.hasPerspective());
203 this->setBounds(viewMatrix.mapRect(rrect.getBounds()),
204 GrOp::HasAABloat(!(processorFlags & ProcessorFlags::kFakeNonAA)),
Chris Dalton4f447342021-03-12 12:09:12 -0700205 GrOp::IsHairline::kNo);
Chris Dalton133944a2018-11-16 23:30:29 -0500206}
207
Chris Daltonea46ef32021-07-12 15:09:06 -0600208GrDrawOp::ClipResult FillRRectOp::clipToShape(GrSurfaceDrawContext* sdc, SkClipOp clipOp,
209 const SkMatrix& clipMatrix, const GrShape& shape,
210 GrAA aa) {
211 SkASSERT(fInstanceCount == 1); // This needs to be called before combining.
212 SkASSERT(fHeadInstance->fNext == nullptr);
213
214 if ((shape.isRect() || shape.isRRect()) &&
215 clipOp == SkClipOp::kIntersect &&
216 (aa == GrAA::kNo) == (fProcessorFlags & ProcessorFlags::kFakeNonAA)) {
217 // The clip shape is a round rect. Attempt to map it to a round rect in "viewMatrix" space.
218 SkRRect clipRRect;
219 if (clipMatrix == fHeadInstance->fViewMatrix) {
220 if (shape.isRect()) {
221 clipRRect.setRect(shape.rect());
222 } else {
223 clipRRect = shape.rrect();
224 }
225 } else {
226 // Find a matrix that maps from "clipMatrix" space to "viewMatrix" space.
227 SkASSERT(!fHeadInstance->fViewMatrix.hasPerspective());
228 if (clipMatrix.hasPerspective()) {
229 return ClipResult::kFail;
230 }
231 SkMatrix clipToView;
232 if (!fHeadInstance->fViewMatrix.invert(&clipToView)) {
233 return ClipResult::kClippedOut;
234 }
235 clipToView.preConcat(clipMatrix);
236 SkASSERT(!clipToView.hasPerspective());
237 if (!SkScalarNearlyZero(clipToView.getSkewX()) ||
238 !SkScalarNearlyZero(clipToView.getSkewY())) {
239 // A rect in "clipMatrix" space is not a rect in "viewMatrix" space.
240 return ClipResult::kFail;
241 }
242 clipToView.setSkewX(0);
243 clipToView.setSkewY(0);
244 SkASSERT(clipToView.rectStaysRect());
245
246 if (shape.isRect()) {
247 clipRRect.setRect(clipToView.mapRect(shape.rect()));
248 } else {
249 if (!shape.rrect().transform(clipToView, &clipRRect)) {
250 // Transforming the rrect failed. This shouldn't generally happen except in
251 // cases of fp32 overflow.
252 return ClipResult::kFail;
253 }
254 }
255 }
256
257 // Intersect our round rect with the clip shape.
258 SkRRect isectRRect;
259 if (fHeadInstance->fRRect.isRect() && clipRRect.isRect()) {
260 SkRect isectRect;
261 if (!isectRect.intersect(fHeadInstance->fRRect.rect(), clipRRect.rect())) {
262 return ClipResult::kClippedOut;
263 }
264 isectRRect.setRect(isectRect);
265 } else {
266 isectRRect = SkRRectPriv::ConservativeIntersect(fHeadInstance->fRRect, clipRRect);
267 if (isectRRect.isEmpty()) {
268 // The round rects did not intersect at all or the intersection was too complicated
269 // to compute quickly.
270 return ClipResult::kFail;
271 }
272 }
273
274 // Update the local rect.
275 auto rect = skvx::bit_pun<grvx::float4>(fHeadInstance->fRRect.rect());
276 auto local = skvx::bit_pun<grvx::float4>(fHeadInstance->fLocalRect);
277 auto isect = skvx::bit_pun<grvx::float4>(isectRRect.rect());
278 auto rectToLocalSize = (local - skvx::shuffle<2,3,0,1>(local)) /
279 (rect - skvx::shuffle<2,3,0,1>(rect));
280 fHeadInstance->fLocalRect = skvx::bit_pun<SkRect>((isect - rect) * rectToLocalSize + local);
281
282 // Update the round rect.
283 fHeadInstance->fRRect = isectRRect;
284 return ClipResult::kClippedGeometrically;
285 }
286
287 return ClipResult::kFail;
288}
289
Chris Dalton57ab06c2021-04-22 12:57:28 -0600290GrProcessorSet::Analysis FillRRectOp::finalize(const GrCaps& caps, const GrAppliedClip* clip,
291 GrClampType clampType) {
Chris Dalton3c636f02021-07-08 14:50:57 -0600292 SkASSERT(fInstanceCount == 1);
293 SkASSERT(fHeadInstance->fNext == nullptr);
Chris Dalton133944a2018-11-16 23:30:29 -0500294
Robert Phillips360ec182020-03-26 13:29:50 -0400295 bool isWideColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -0600296 auto analysis = fHelper.finalizeProcessors(caps, clip, clampType,
Chris Dalton3c636f02021-07-08 14:50:57 -0600297 GrProcessorAnalysisCoverage::kSingleChannel,
298 &fHeadInstance->fColor, &isWideColor);
Robert Phillips360ec182020-03-26 13:29:50 -0400299 if (isWideColor) {
300 fProcessorFlags |= ProcessorFlags::kWideColor;
Brian Osman5105d682019-02-13 16:06:14 -0500301 }
Chris Dalton133944a2018-11-16 23:30:29 -0500302 if (analysis.usesLocalCoords()) {
Robert Phillips360ec182020-03-26 13:29:50 -0400303 fProcessorFlags |= ProcessorFlags::kHasLocalCoords;
Chris Dalton133944a2018-11-16 23:30:29 -0500304 }
Chris Dalton4b62aed2019-01-15 11:53:00 -0700305 return analysis;
Chris Dalton133944a2018-11-16 23:30:29 -0500306}
307
Herb Derbye25c3002020-10-27 15:57:27 -0400308GrOp::CombineResult FillRRectOp::onCombineIfPossible(GrOp* op, SkArenaAlloc*, const GrCaps& caps) {
Robert Phillips366176b2020-02-26 11:40:50 -0500309 const auto& that = *op->cast<FillRRectOp>();
Chris Dalton3c636f02021-07-08 14:50:57 -0600310 if (!fHelper.isCompatible(that.fHelper, caps, this->bounds(), that.bounds()) ||
311 fProcessorFlags != that.fProcessorFlags) {
Robert Phillips360ec182020-03-26 13:29:50 -0400312 return CombineResult::kCannotCombine;
313 }
314
Chris Dalton3c636f02021-07-08 14:50:57 -0600315 *fTailInstance = that.fHeadInstance;
316 fTailInstance = that.fTailInstance;
Chris Dalton133944a2018-11-16 23:30:29 -0500317 fInstanceCount += that.fInstanceCount;
Chris Dalton133944a2018-11-16 23:30:29 -0500318 return CombineResult::kMerged;
319}
320
Robert Phillips366176b2020-02-26 11:40:50 -0500321class FillRRectOp::Processor : public GrGeometryProcessor {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600322public:
Robert Phillips360ec182020-03-26 13:29:50 -0400323 static GrGeometryProcessor* Make(SkArenaAlloc* arena, GrAAType aaType, ProcessorFlags flags) {
Mike Kleinf1241082020-12-14 15:59:09 -0600324 return arena->make([&](void* ptr) {
325 return new (ptr) Processor(aaType, flags);
326 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500327 }
328
Robert Phillips8053c972019-11-21 10:44:53 -0500329 const char* name() const final { return "GrFillRRectOp::Processor"; }
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500330
Robert Phillips8053c972019-11-21 10:44:53 -0500331 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const final {
Chris Daltoncc13b352021-03-05 14:59:01 -0700332 b->addBits(kNumProcessorFlags, (uint32_t)fFlags, "flags");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500333 }
334
Robert Phillipsf10535f2021-03-23 09:30:45 -0400335 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500336
337private:
Robert Phillips360ec182020-03-26 13:29:50 -0400338 Processor(GrAAType aaType, ProcessorFlags flags)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500339 : INHERITED(kGrFillRRectOp_Processor_ClassID)
Chris Dalton0dffbab2019-03-27 13:08:50 -0600340 , fFlags(flags) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700341 this->setVertexAttributes(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
Chris Dalton133944a2018-11-16 23:30:29 -0500342
Chris Daltoncc13b352021-03-05 14:59:01 -0700343 fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
344 fInstanceAttribs.emplace_back("translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600345 fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
346 fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
347 fColorAttrib = &fInstanceAttribs.push_back(
Robert Phillips360ec182020-03-26 13:29:50 -0400348 MakeColorAttribute("color", (fFlags & ProcessorFlags::kWideColor)));
349 if (fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600350 fInstanceAttribs.emplace_back(
351 "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
352 }
Chris Dalton3c636f02021-07-08 14:50:57 -0600353 SkASSERT(fInstanceAttribs.count() <= kMaxInstanceAttribs);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600354 this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
Chris Dalton0dffbab2019-03-27 13:08:50 -0600355 }
356
Chris Dalton0dffbab2019-03-27 13:08:50 -0600357 static constexpr Attribute kVertexAttribs[] = {
358 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
359 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
360 // Coverage only.
361 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
362
Robert Phillips360ec182020-03-26 13:29:50 -0400363 const ProcessorFlags fFlags;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600364
Chris Dalton3c636f02021-07-08 14:50:57 -0600365 constexpr static int kMaxInstanceAttribs = 6;
366 SkSTArray<kMaxInstanceAttribs, Attribute> fInstanceAttribs;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600367 const Attribute* fColorAttrib;
368
Chris Daltoncc13b352021-03-05 14:59:01 -0700369 class Impl;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500370
John Stiles7571f9e2020-09-02 22:42:33 -0400371 using INHERITED = GrGeometryProcessor;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600372};
373
Robert Phillips787fd9d2021-03-22 14:48:09 -0400374constexpr GrGeometryProcessor::Attribute FillRRectOp::Processor::kVertexAttribs[];
Chris Dalton0dffbab2019-03-27 13:08:50 -0600375
376// Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
Chris Dalton133944a2018-11-16 23:30:29 -0500377// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
378// edges. The Vertex struct tells the shader where to place its vertex within a normalized
379// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600380struct CoverageVertex {
Chris Dalton133944a2018-11-16 23:30:29 -0500381 std::array<float, 4> fRadiiSelector;
382 std::array<float, 2> fCorner;
383 std::array<float, 2> fRadiusOutset;
384 std::array<float, 2> fAABloatDirection;
385 float fCoverage;
386 float fIsLinearCoverage;
Chris Dalton133944a2018-11-16 23:30:29 -0500387};
388
389// This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
390// of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
391// rectangles.
392static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
393
Chris Daltoncc13b352021-03-05 14:59:01 -0700394static constexpr CoverageVertex kVertexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500395 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700396 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
397 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500398
399 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700400 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
401 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500402
403 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700404 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
405 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500406
407 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700408 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
409 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500410
411
412 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700413 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
414 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500415
416 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700417 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
418 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500419
420 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700421 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
422 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500423
424 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700425 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
426 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500427
428
429 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700430 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
431 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
432 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
433 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
434 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
435 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500436
437 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700438 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
439 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
440 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
441 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
442 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
443 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500444
445 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700446 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
447 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
448 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
449 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
450 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
451 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500452
453 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700454 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
455 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
456 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
457 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
Chris Dalton2d07e862018-11-26 12:30:47 -0700458 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700459 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500460
Chris Daltoncc13b352021-03-05 14:59:01 -0700461GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500462
Chris Daltoncc13b352021-03-05 14:59:01 -0700463static constexpr uint16_t kIndexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500464 // Inset octagon (solid coverage).
465 0, 1, 7,
466 1, 2, 7,
467 7, 2, 6,
468 2, 3, 6,
469 6, 3, 5,
470 3, 4, 5,
471
472 // AA borders (linear coverage).
473 0, 1, 8, 1, 9, 8,
474 2, 3, 10, 3, 11, 10,
475 4, 5, 12, 5, 13, 12,
476 6, 7, 14, 7, 15, 14,
477
478 // Top-left arc.
479 16, 17, 21,
480 17, 21, 18,
481 21, 18, 20,
482 18, 20, 19,
483
484 // Top-right arc.
485 22, 23, 27,
486 23, 27, 24,
487 27, 24, 26,
488 24, 26, 25,
489
490 // Bottom-right arc.
491 28, 29, 33,
492 29, 33, 30,
493 33, 30, 32,
494 30, 32, 31,
495
496 // Bottom-left arc.
497 34, 35, 39,
498 35, 39, 36,
499 39, 36, 38,
500 36, 38, 37};
501
Chris Daltoncc13b352021-03-05 14:59:01 -0700502GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400503
Robert Phillips71143952021-06-17 14:55:07 -0400504void FillRRectOp::onPrepareDraws(GrMeshDrawTarget* target) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700505 // We request no multisample, but some platforms don't support disabling it on MSAA targets.
Chris Dalton2517ce32021-04-13 00:21:15 -0600506 if (target->usesMSAASurface() && !target->caps().multisampleDisableSupport()) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700507 fProcessorFlags |= ProcessorFlags::kMSAAEnabled;
508 }
509
Chris Dalton3c636f02021-07-08 14:50:57 -0600510 if (!fProgramInfo) {
511 this->createProgramInfo(target);
512 }
513
514 size_t instanceStride = fProgramInfo->geomProc().instanceStride();
515
516 if (GrVertexWriter instanceWrter = target->makeVertexSpace(instanceStride, fInstanceCount,
517 &fInstanceBuffer, &fBaseInstance)) {
518 SkDEBUGCODE(auto end = instanceWrter.makeOffset(instanceStride * fInstanceCount));
519 for (Instance* i = fHeadInstance; i; i = i->fNext) {
520 auto [l, t, r, b] = i->fRRect.rect();
521
522 // Produce a matrix that draws the round rect from normalized [-1, -1, +1, +1] space.
523 SkMatrix m;
524 // Unmap the normalized rect [-1, -1, +1, +1] back to [l, t, r, b].
525 m.setScaleTranslate((r - l)/2, (b - t)/2, (l + r)/2, (t + b)/2);
526 // Map to device space.
527 m.postConcat(i->fViewMatrix);
528
529 // Convert the radii to [-1, -1, +1, +1] space and write their attribs.
530 grvx::float4 radiiX, radiiY;
531 grvx::strided_load2(&SkRRectPriv::GetRadiiArray(i->fRRect)->fX, radiiX, radiiY);
532 radiiX *= 2 / (r - l);
533 radiiY *= 2 / (b - t);
534
535 instanceWrter.write(
536 m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY(),
537 m.getTranslateX(), m.getTranslateY(),
538 radiiX,
539 radiiY,
540 GrVertexColor(i->fColor, fProcessorFlags & ProcessorFlags::kWideColor),
541 GrVertexWriter::If(fProcessorFlags & ProcessorFlags::kHasLocalCoords,
542 i->fLocalRect));
543 }
544 SkASSERT(instanceWrter == end);
Greg Danielf793de12019-09-05 13:23:23 -0400545 }
546
Chris Daltoncc13b352021-03-05 14:59:01 -0700547 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400548
Chris Daltoncc13b352021-03-05 14:59:01 -0700549 fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kIndex,
550 sizeof(kIndexData),
551 kIndexData, gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400552
Chris Daltoncc13b352021-03-05 14:59:01 -0700553 GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400554
Chris Daltoncc13b352021-03-05 14:59:01 -0700555 fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,
556 sizeof(kVertexData),
557 kVertexData,
558 gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400559}
560
Chris Daltoncc13b352021-03-05 14:59:01 -0700561class FillRRectOp::Processor::Impl : public GrGLSLGeometryProcessor {
Chris Dalton133944a2018-11-16 23:30:29 -0500562 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
John Stiles4d7ac492021-03-09 20:16:43 -0500563 GrGLSLVertexBuilder* v = args.fVertBuilder;
564 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
565
Robert Phillips787fd9d2021-03-22 14:48:09 -0400566 const auto& proc = args.fGeomProc.cast<Processor>();
Robert Phillips360ec182020-03-26 13:29:50 -0400567 bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
Chris Dalton133944a2018-11-16 23:30:29 -0500568
Chris Dalton0dffbab2019-03-27 13:08:50 -0600569 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
570
Chris Dalton133944a2018-11-16 23:30:29 -0500571 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
572 varyings->emitAttributes(proc);
John Stiles4d7ac492021-03-09 20:16:43 -0500573 f->codeAppendf("half4 %s;", args.fOutputColor);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600574 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500575 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
576
577 // Emit the vertex shader.
Chris Daltoncc13b352021-03-05 14:59:01 -0700578 // When MSAA is enabled, we need to make sure every sample gets lit up on pixels that have
579 // fractional coverage. We do this by making the ramp wider.
580 v->codeAppendf("float aa_bloat_multiplier = %i;",
581 (proc.fFlags & ProcessorFlags::kMSAAEnabled)
582 ? 2 // Outset an entire pixel (2 radii).
583 : (!(proc.fFlags & ProcessorFlags::kFakeNonAA))
584 ? 1 // Outset one half pixel (1 radius).
585 : 0); // No AA bloat.
586
Chris Dalton133944a2018-11-16 23:30:29 -0500587 // Unpack vertex attribs.
588 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
589 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
590 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
Chris Dalton133944a2018-11-16 23:30:29 -0500591 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
592
593 // Find the amount to bloat each edge for AA (in source space).
594 v->codeAppend("float2 pixellength = inversesqrt("
595 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
596 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
597 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
598 "abs(normalized_axis_dirs.zw));");
599 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
600
601 // Identify our radii.
Mike Reedd3efa992018-11-28 13:13:15 +0000602 v->codeAppend("float4 radii_and_neighbors = radii_selector"
603 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
604 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
605 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500606
Chris Daltoncc13b352021-03-05 14:59:01 -0700607 v->codeAppend("float coverage_multiplier = 1;");
Chris Dalton133944a2018-11-16 23:30:29 -0500608 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700609 // The rrect is more narrow than a half-pixel AA coverage ramp. We can't
610 // draw as-is or else opposite AA borders will overlap. Instead, fudge the
611 // size up to the width of a coverage ramp, and then reduce total coverage
612 // to make the rect appear more thin.
Chris Dalton133944a2018-11-16 23:30:29 -0500613 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
Chris Daltoncc13b352021-03-05 14:59:01 -0700614 v->codeAppend( "coverage_multiplier = 1 / (max(aa_bloatradius.x, 1) * "
615 "max(aa_bloatradius.y, 1));");
Chris Dalton133944a2018-11-16 23:30:29 -0500616 // Set radii to zero to ensure we take the "linear coverage" codepath.
617 // (The "coverage" variable only has effect in the linear codepath.)
618 v->codeAppend( "radii = float2(0);");
619 v->codeAppend("}");
620
Chris Daltoncc13b352021-03-05 14:59:01 -0700621 // Unpack coverage.
622 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
623 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
624 // MSAA has a wider ramp that goes from -.5 to 1.5 instead of 0 to 1.
625 v->codeAppendf("coverage = (coverage - .5) * aa_bloat_multiplier + .5;");
626 }
627
628 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.5))) {");
Chris Dalton133944a2018-11-16 23:30:29 -0500629 // The radii are very small. Demote this arc to a sharp 90 degree corner.
Chris Daltoncc13b352021-03-05 14:59:01 -0700630 v->codeAppend( "radii = float2(0);");
631 // Convert to a standard picture frame for an AA rect instead of the round
632 // rect geometry.
633 v->codeAppend( "aa_bloat_direction = sign(corner);");
634 v->codeAppend( "if (coverage > .5) {"); // Are we an inset edge?
635 v->codeAppend( "aa_bloat_direction = -aa_bloat_direction;");
636 v->codeAppend( "}");
Chris Dalton133944a2018-11-16 23:30:29 -0500637 v->codeAppend( "is_linear_coverage = 1;");
638 v->codeAppend("} else {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700639 // Don't let radii get smaller than a coverage ramp plus an extra half
640 // pixel for MSAA. Always use the same amount so we don't pop when
641 // switching between MSAA and coverage.
642 v->codeAppend( "radii = clamp(radii, pixellength * 1.5, 2 - pixellength * 1.5);");
643 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength * 1.5, "
644 "2 - pixellength * 1.5);");
Mike Reedd3efa992018-11-28 13:13:15 +0000645 // Don't let neighboring radii get closer together than 1/16 pixel.
646 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
647 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
648 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500649 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500650
651 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
652 // normalized [-1,-1,+1,+1] space.
Chris Daltoncc13b352021-03-05 14:59:01 -0700653 v->codeAppend("float2 aa_outset = "
654 "aa_bloat_direction * aa_bloatradius * aa_bloat_multiplier;");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700655 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500656
Chris Daltoncc13b352021-03-05 14:59:01 -0700657 v->codeAppend("if (coverage > .5) {"); // Are we an inset edge?
658 // Don't allow the aa insets to overlap. i.e., Don't let them inset past
659 // the center (x=y=0). Since we don't allow the rect to become thinner
660 // than 1px, this should only happen when using MSAA, where we inset by an
661 // entire pixel instead of half.
662 v->codeAppend( "if (aa_bloat_direction.x != 0 && vertexpos.x * corner.x < 0) {");
663 v->codeAppend( "float backset = abs(vertexpos.x);");
664 v->codeAppend( "vertexpos.x = 0;");
665 v->codeAppend( "vertexpos.y += "
666 "backset * sign(corner.y) * pixellength.y/pixellength.x;");
667 v->codeAppend( "coverage = (coverage - .5) * abs(corner.x) / "
668 "(abs(corner.x) + backset) + .5;");
669 v->codeAppend( "}");
670 v->codeAppend( "if (aa_bloat_direction.y != 0 && vertexpos.y * corner.y < 0) {");
671 v->codeAppend( "float backset = abs(vertexpos.y);");
672 v->codeAppend( "vertexpos.y = 0;");
673 v->codeAppend( "vertexpos.x += "
674 "backset * sign(corner.x) * pixellength.x/pixellength.y;");
675 v->codeAppend( "coverage = (coverage - .5) * abs(corner.y) / "
676 "(abs(corner.y) + backset) + .5;");
677 v->codeAppend( "}");
678 v->codeAppend("}");
679
Michael Ludwig553db622020-06-19 10:47:30 -0400680 // Write positions
Chris Dalton133944a2018-11-16 23:30:29 -0500681 GrShaderVar localCoord("", kFloat2_GrSLType);
Robert Phillips360ec182020-03-26 13:29:50 -0400682 if (proc.fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton133944a2018-11-16 23:30:29 -0500683 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
684 "local_rect.zw * (1 + vertexpos)) * .5;");
Michael Ludwig553db622020-06-19 10:47:30 -0400685 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
Chris Dalton133944a2018-11-16 23:30:29 -0500686 }
Chris Dalton133944a2018-11-16 23:30:29 -0500687
688 // Transform to device space.
689 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
690 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
691 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
692
693 // Setup interpolants for coverage.
694 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
695 varyings->addVarying("arccoord", &arcCoord);
696 v->codeAppend("if (0 != is_linear_coverage) {");
697 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
698 // interpolate linear coverage across y.
Chris Daltoncc13b352021-03-05 14:59:01 -0700699 v->codeAppendf( "%s.xy = float2(0, coverage * coverage_multiplier);",
700 arcCoord.vsOut());
Chris Dalton133944a2018-11-16 23:30:29 -0500701 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700702 // Find the normalized arc coordinates for our corner ellipse.
703 // (i.e., the coordinate system where x^2 + y^2 == 1).
704 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500705 // We are a corner piece: Interpolate the arc coordinates for coverage.
706 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
707 // instructs the fragment shader to use linear coverage).
708 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
709 if (!useHWDerivatives) {
710 // The gradient is order-1: Interpolate it across arccoord.zw.
711 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
712 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
713 }
714 v->codeAppend("}");
715
716 // Emit the fragment shader.
Chris Dalton133944a2018-11-16 23:30:29 -0500717 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
718 f->codeAppendf("half coverage;");
719 f->codeAppendf("if (0 == x_plus_1) {");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600720 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500721 f->codeAppendf("} else {");
722 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
723 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
724 if (useHWDerivatives) {
725 f->codeAppendf("float fnwidth = fwidth(fn);");
726 } else {
727 // The gradient is interpolated across arccoord.zw.
728 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
729 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
730 }
Chris Daltoncc13b352021-03-05 14:59:01 -0700731 f->codeAppendf( "coverage = .5 - half(fn/fnwidth);");
732 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
733 // MSAA uses ramps larger than 1px, so we need to clamp in both branches.
734 f->codeAppendf("}");
735 }
736 f->codeAppendf("coverage = clamp(coverage, 0, 1);");
737 if (!(proc.fFlags & ProcessorFlags::kMSAAEnabled)) {
738 // When not using MSAA, we only need to clamp in the "arc" branch.
739 f->codeAppendf("}");
740 }
741 if (proc.fFlags & ProcessorFlags::kFakeNonAA) {
742 f->codeAppendf("coverage = (coverage >= .5) ? 1 : 0;");
743 }
John Stiles4d7ac492021-03-09 20:16:43 -0500744 f->codeAppendf("half4 %s = half4(coverage);", args.fOutputCoverage);
Chris Dalton133944a2018-11-16 23:30:29 -0500745 }
746
Brian Salomon5a328282021-04-14 10:32:25 -0400747 void setData(const GrGLSLProgramDataManager&,
748 const GrShaderCaps&,
749 const GrGeometryProcessor&) override {}
Chris Dalton133944a2018-11-16 23:30:29 -0500750};
751
Chris Dalton0dffbab2019-03-27 13:08:50 -0600752
Robert Phillipsf10535f2021-03-23 09:30:45 -0400753GrGLSLGeometryProcessor* FillRRectOp::Processor::createGLSLInstance(const GrShaderCaps&) const {
Chris Daltoncc13b352021-03-05 14:59:01 -0700754 return new Impl();
Chris Dalton133944a2018-11-16 23:30:29 -0500755}
756
Robert Phillipscad8fba2020-03-20 15:39:29 -0400757void FillRRectOp::onCreateProgramInfo(const GrCaps* caps,
758 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -0500759 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400760 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -0400761 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500762 GrXferBarrierFlags renderPassXferBarriers,
763 GrLoadOp colorLoadOp) {
Robert Phillips360ec182020-03-26 13:29:50 -0400764 GrGeometryProcessor* gp = Processor::Make(arena, fHelper.aaType(), fProcessorFlags);
Brian Salomon8afde5f2020-04-01 16:22:00 -0400765 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -0400766 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -0500767 renderPassXferBarriers, colorLoadOp);
Robert Phillips8053c972019-11-21 10:44:53 -0500768}
769
Robert Phillips366176b2020-02-26 11:40:50 -0500770void FillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Robert Phillips8053c972019-11-21 10:44:53 -0500771 if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
772 return; // Setup failed.
773 }
774
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600775 flushState->bindPipelineAndScissorClip(*fProgramInfo, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400776 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Greg Daniel426274b2020-07-20 11:37:38 -0400777 flushState->bindBuffers(std::move(fIndexBuffer), std::move(fInstanceBuffer),
778 std::move(fVertexBuffer));
Chris Daltoncc13b352021-03-05 14:59:01 -0700779 flushState->drawIndexedInstanced(SK_ARRAY_COUNT(kIndexData), 0, fInstanceCount, fBaseInstance,
780 0);
Chris Dalton133944a2018-11-16 23:30:29 -0500781}
782
783// Will the given corner look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600784static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
Chris Dalton133944a2018-11-16 23:30:29 -0500785 Sk2f devRadii = devScale * cornerRadii;
786 if (devRadii[1] < devRadii[0]) {
787 devRadii = SkNx_shuffle<1,0>(devRadii);
788 }
Brian Osman788b9162020-02-07 10:36:46 -0500789 float minDevRadius = std::max(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
Chris Dalton133944a2018-11-16 23:30:29 -0500790 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
791 // This threshold was arrived at subjevtively on an NVIDIA chip.
792 return minDevRadius * minDevRadius * 5 > devRadii[1];
793}
794
Chris Dalton0dffbab2019-03-27 13:08:50 -0600795static bool can_use_hw_derivatives_with_coverage(
796 const Sk2f& devScale, const SkVector& cornerRadii) {
797 return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
Chris Dalton133944a2018-11-16 23:30:29 -0500798}
799
800// Will the given round rect look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600801static bool can_use_hw_derivatives_with_coverage(
802 const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
Chris Dalton133944a2018-11-16 23:30:29 -0500803 if (!shaderCaps.shaderDerivativeSupport()) {
804 return false;
805 }
806
807 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
808 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
809 Sk2f devScale = (x*x + y*y).sqrt();
810 switch (rrect.getType()) {
811 case SkRRect::kEmpty_Type:
812 case SkRRect::kRect_Type:
813 return true;
814
815 case SkRRect::kOval_Type:
816 case SkRRect::kSimple_Type:
Chris Dalton0dffbab2019-03-27 13:08:50 -0600817 return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
Chris Dalton133944a2018-11-16 23:30:29 -0500818
819 case SkRRect::kNinePatch_Type: {
820 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
821 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
822 Sk2f minRadii = Sk2f::Min(r0, r1);
823 Sk2f maxRadii = Sk2f::Max(r0, r1);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600824 return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
825 can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
Chris Dalton133944a2018-11-16 23:30:29 -0500826 }
827
828 case SkRRect::kComplex_Type: {
829 for (int i = 0; i < 4; ++i) {
830 auto corner = static_cast<SkRRect::Corner>(i);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600831 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
Chris Dalton133944a2018-11-16 23:30:29 -0500832 return false;
833 }
834 }
835 return true;
836 }
837 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600838 SK_ABORT("Invalid round rect type.");
Chris Dalton133944a2018-11-16 23:30:29 -0500839}
Robert Phillips366176b2020-02-26 11:40:50 -0500840
841} // anonymous namespace
842
843
Herb Derbyc76d4092020-10-07 16:46:15 -0400844GrOp::Owner GrFillRRectOp::Make(GrRecordingContext* ctx,
Chris Dalton3c636f02021-07-08 14:50:57 -0600845 SkArenaAlloc* arena,
Herb Derbyc76d4092020-10-07 16:46:15 -0400846 GrPaint&& paint,
847 const SkMatrix& viewMatrix,
848 const SkRRect& rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600849 const SkRect& localRect,
Chris Dalton4f447342021-03-12 12:09:12 -0700850 GrAA aa) {
Chris Dalton3c636f02021-07-08 14:50:57 -0600851 return FillRRectOp::Make(ctx, arena, std::move(paint), viewMatrix, rrect, localRect, aa);
Robert Phillips366176b2020-02-26 11:40:50 -0500852}
853
Chris Dalton61d694d2021-03-22 00:00:52 -0600854
Robert Phillips366176b2020-02-26 11:40:50 -0500855#if GR_TEST_UTILS
856
857#include "src/gpu/GrDrawOpTest.h"
858
859GR_DRAW_OP_TEST_DEFINE(FillRRectOp) {
Chris Dalton3c636f02021-07-08 14:50:57 -0600860 SkArenaAlloc arena(64 * sizeof(float));
Robert Phillips366176b2020-02-26 11:40:50 -0500861 SkMatrix viewMatrix = GrTest::TestMatrix(random);
Chris Dalton4f447342021-03-12 12:09:12 -0700862 GrAA aa = GrAA(random->nextBool());
Robert Phillips366176b2020-02-26 11:40:50 -0500863
864 SkRect rect = GrTest::TestRect(random);
865 float w = rect.width();
866 float h = rect.height();
867
868 SkRRect rrect;
869 // TODO: test out other rrect configurations
870 rrect.setNinePatch(rect, w / 3.0f, h / 4.0f, w / 5.0f, h / 6.0);
871
872 return GrFillRRectOp::Make(context,
Chris Dalton3c636f02021-07-08 14:50:57 -0600873 &arena,
Robert Phillips360ec182020-03-26 13:29:50 -0400874 std::move(paint),
Robert Phillips366176b2020-02-26 11:40:50 -0500875 viewMatrix,
876 rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600877 rrect.rect(),
Chris Dalton4f447342021-03-12 12:09:12 -0700878 aa);
Robert Phillips366176b2020-02-26 11:40:50 -0500879}
880
881#endif