blob: 54a829ef70494dd66479daa4352abde5e058047a [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,
Chris Dalton6aaf00f2021-07-13 13:26:39 -0600100 bool usesMSAASurface,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400101 GrAppliedClip&&,
John Stiles52cb1d02021-06-02 11:58:05 -0400102 const GrDstProxyView&,
Greg Daniel42dbca52020-11-20 10:22:43 -0500103 GrXferBarrierFlags renderPassXferBarriers,
104 GrLoadOp colorLoadOp) final;
Robert Phillips366176b2020-02-26 11:40:50 -0500105
Robert Phillips360ec182020-03-26 13:29:50 -0400106 Helper fHelper;
Robert Phillips360ec182020-03-26 13:29:50 -0400107 ProcessorFlags fProcessorFlags;
Robert Phillips366176b2020-02-26 11:40:50 -0500108
Chris Dalton3c636f02021-07-08 14:50:57 -0600109 struct Instance {
110 Instance(const SkMatrix& viewMatrix, const SkRRect& rrect, const SkRect& localRect,
111 const SkPMColor4f& color)
112 : fViewMatrix(viewMatrix), fRRect(rrect), fLocalRect(localRect), fColor(color) {}
113 SkMatrix fViewMatrix;
114 SkRRect fRRect;
115 SkRect fLocalRect;
116 SkPMColor4f fColor;
117 Instance* fNext = nullptr;
118 };
119
120 Instance* fHeadInstance;
121 Instance** fTailInstance;
Robert Phillips366176b2020-02-26 11:40:50 -0500122 int fInstanceCount = 1;
Robert Phillips366176b2020-02-26 11:40:50 -0500123
124 sk_sp<const GrBuffer> fInstanceBuffer;
125 sk_sp<const GrBuffer> fVertexBuffer;
126 sk_sp<const GrBuffer> fIndexBuffer;
127 int fBaseInstance = 0;
Robert Phillips366176b2020-02-26 11:40:50 -0500128
129 // If this op is prePrepared the created programInfo will be stored here for use in
130 // onExecute. In the prePrepared case it will have been stored in the record-time arena.
131 GrProgramInfo* fProgramInfo = nullptr;
132
John Stiles7571f9e2020-09-02 22:42:33 -0400133 using INHERITED = GrMeshDrawOp;
Robert Phillips366176b2020-02-26 11:40:50 -0500134};
135
Robert Phillips360ec182020-03-26 13:29:50 -0400136GR_MAKE_BITFIELD_CLASS_OPS(FillRRectOp::ProcessorFlags)
Chris Dalton133944a2018-11-16 23:30:29 -0500137
138// Hardware derivatives are not always accurate enough for highly elliptical corners. This method
139// checks to make sure the corners will still all look good if we use HW derivatives.
Robert Phillips360ec182020-03-26 13:29:50 -0400140static bool can_use_hw_derivatives_with_coverage(const GrShaderCaps&,
141 const SkMatrix&,
142 const SkRRect&);
Chris Dalton133944a2018-11-16 23:30:29 -0500143
Herb Derbyc76d4092020-10-07 16:46:15 -0400144GrOp::Owner FillRRectOp::Make(GrRecordingContext* ctx,
Chris Dalton3c636f02021-07-08 14:50:57 -0600145 SkArenaAlloc* arena,
Herb Derbyc76d4092020-10-07 16:46:15 -0400146 GrPaint&& paint,
147 const SkMatrix& viewMatrix,
148 const SkRRect& rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600149 const SkRect& localRect,
Chris Dalton4f447342021-03-12 12:09:12 -0700150 GrAA aa) {
Robert Phillips360ec182020-03-26 13:29:50 -0400151 using Helper = GrSimpleMeshDrawOpHelper;
152
153 const GrCaps* caps = ctx->priv().caps();
154
Chris Daltona77cdee2020-04-03 14:50:43 -0600155 if (!caps->drawInstancedSupport()) {
Chris Dalton133944a2018-11-16 23:30:29 -0500156 return nullptr;
157 }
158
Chris Dalton61d694d2021-03-22 00:00:52 -0600159 // We transform into a normalized -1..+1 space to draw the round rect. If the boundaries are too
160 // large, the math can overflow. The caller can fall back on path rendering if this is the case.
161 if (std::max(rrect.height(), rrect.width()) >= 1e6f) {
162 return nullptr;
163 }
164
Robert Phillips360ec182020-03-26 13:29:50 -0400165 ProcessorFlags flags = ProcessorFlags::kNone;
Chris Daltoncc13b352021-03-05 14:59:01 -0700166 // TODO: Support perspective in a follow-on CL. This shouldn't be difficult, since we already
167 // use HW derivatives. The only trick will be adjusting the AA outset to account for
168 // perspective. (i.e., outset = 0.5 * z.)
169 if (viewMatrix.hasPerspective()) {
170 return nullptr;
171 }
172 if (can_use_hw_derivatives_with_coverage(*caps->shaderCaps(), viewMatrix, rrect)) {
173 // HW derivatives (more specifically, fwidth()) are consistently faster on all platforms in
174 // coverage mode. We use them as long as the approximation will be accurate enough.
175 flags |= ProcessorFlags::kUseHWDerivatives;
176 }
Chris Dalton4f447342021-03-12 12:09:12 -0700177 if (aa == GrAA::kNo) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700178 flags |= ProcessorFlags::kFakeNonAA;
Chris Dalton133944a2018-11-16 23:30:29 -0500179 }
180
Chris Dalton3c636f02021-07-08 14:50:57 -0600181 return Helper::FactoryHelper<FillRRectOp>(ctx, std::move(paint), arena, viewMatrix, rrect,
182 localRect, flags);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600183}
184
Herb Derbyc76d4092020-10-07 16:46:15 -0400185FillRRectOp::FillRRectOp(GrProcessorSet* processorSet,
Robert Phillips360ec182020-03-26 13:29:50 -0400186 const SkPMColor4f& paintColor,
Chris Dalton3c636f02021-07-08 14:50:57 -0600187 SkArenaAlloc* arena,
188 const SkMatrix& viewMatrix,
Robert Phillips360ec182020-03-26 13:29:50 -0400189 const SkRRect& rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600190 const SkRect& localRect,
Chris Dalton3c636f02021-07-08 14:50:57 -0600191 ProcessorFlags processorFlags)
Robert Phillipscad8fba2020-03-20 15:39:29 -0400192 : INHERITED(ClassID())
Chris Daltoncc13b352021-03-05 14:59:01 -0700193 , fHelper(processorSet,
194 (processorFlags & ProcessorFlags::kFakeNonAA)
195 ? GrAAType::kNone
196 : GrAAType::kCoverage) // Use analytic AA even if the RT is MSAA.
Robert Phillips360ec182020-03-26 13:29:50 -0400197 , fProcessorFlags(processorFlags & ~(ProcessorFlags::kHasLocalCoords |
Chris Daltoncc13b352021-03-05 14:59:01 -0700198 ProcessorFlags::kWideColor |
Chris Dalton3c636f02021-07-08 14:50:57 -0600199 ProcessorFlags::kMSAAEnabled))
200 , fHeadInstance(arena->make<Instance>(viewMatrix, rrect, localRect, paintColor))
201 , fTailInstance(&fHeadInstance->fNext) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700202 // FillRRectOp::Make fails if there is perspective.
Chris Dalton3c636f02021-07-08 14:50:57 -0600203 SkASSERT(!viewMatrix.hasPerspective());
204 this->setBounds(viewMatrix.mapRect(rrect.getBounds()),
205 GrOp::HasAABloat(!(processorFlags & ProcessorFlags::kFakeNonAA)),
Chris Dalton4f447342021-03-12 12:09:12 -0700206 GrOp::IsHairline::kNo);
Chris Dalton133944a2018-11-16 23:30:29 -0500207}
208
Chris Daltonea46ef32021-07-12 15:09:06 -0600209GrDrawOp::ClipResult FillRRectOp::clipToShape(GrSurfaceDrawContext* sdc, SkClipOp clipOp,
210 const SkMatrix& clipMatrix, const GrShape& shape,
211 GrAA aa) {
212 SkASSERT(fInstanceCount == 1); // This needs to be called before combining.
213 SkASSERT(fHeadInstance->fNext == nullptr);
214
215 if ((shape.isRect() || shape.isRRect()) &&
216 clipOp == SkClipOp::kIntersect &&
217 (aa == GrAA::kNo) == (fProcessorFlags & ProcessorFlags::kFakeNonAA)) {
218 // The clip shape is a round rect. Attempt to map it to a round rect in "viewMatrix" space.
219 SkRRect clipRRect;
220 if (clipMatrix == fHeadInstance->fViewMatrix) {
221 if (shape.isRect()) {
222 clipRRect.setRect(shape.rect());
223 } else {
224 clipRRect = shape.rrect();
225 }
226 } else {
227 // Find a matrix that maps from "clipMatrix" space to "viewMatrix" space.
228 SkASSERT(!fHeadInstance->fViewMatrix.hasPerspective());
229 if (clipMatrix.hasPerspective()) {
230 return ClipResult::kFail;
231 }
232 SkMatrix clipToView;
233 if (!fHeadInstance->fViewMatrix.invert(&clipToView)) {
234 return ClipResult::kClippedOut;
235 }
236 clipToView.preConcat(clipMatrix);
237 SkASSERT(!clipToView.hasPerspective());
238 if (!SkScalarNearlyZero(clipToView.getSkewX()) ||
239 !SkScalarNearlyZero(clipToView.getSkewY())) {
240 // A rect in "clipMatrix" space is not a rect in "viewMatrix" space.
241 return ClipResult::kFail;
242 }
243 clipToView.setSkewX(0);
244 clipToView.setSkewY(0);
245 SkASSERT(clipToView.rectStaysRect());
246
247 if (shape.isRect()) {
248 clipRRect.setRect(clipToView.mapRect(shape.rect()));
249 } else {
250 if (!shape.rrect().transform(clipToView, &clipRRect)) {
251 // Transforming the rrect failed. This shouldn't generally happen except in
252 // cases of fp32 overflow.
253 return ClipResult::kFail;
254 }
255 }
256 }
257
258 // Intersect our round rect with the clip shape.
259 SkRRect isectRRect;
260 if (fHeadInstance->fRRect.isRect() && clipRRect.isRect()) {
261 SkRect isectRect;
262 if (!isectRect.intersect(fHeadInstance->fRRect.rect(), clipRRect.rect())) {
263 return ClipResult::kClippedOut;
264 }
265 isectRRect.setRect(isectRect);
266 } else {
267 isectRRect = SkRRectPriv::ConservativeIntersect(fHeadInstance->fRRect, clipRRect);
268 if (isectRRect.isEmpty()) {
269 // The round rects did not intersect at all or the intersection was too complicated
270 // to compute quickly.
271 return ClipResult::kFail;
272 }
273 }
274
Michael Ludwig575c9212021-07-13 11:09:52 -0400275 // Don't apply the clip geometrically if it becomes subpixel, since then the hairline
276 // rendering may outset beyond the original clip.
277 SkRect devISectBounds = fHeadInstance->fViewMatrix.mapRect(isectRRect.rect());
278 if (devISectBounds.width() < 1.f || devISectBounds.height() < 1.f) {
279 return ClipResult::kFail;
280 }
281
Chris Daltonea46ef32021-07-12 15:09:06 -0600282 // Update the local rect.
283 auto rect = skvx::bit_pun<grvx::float4>(fHeadInstance->fRRect.rect());
284 auto local = skvx::bit_pun<grvx::float4>(fHeadInstance->fLocalRect);
285 auto isect = skvx::bit_pun<grvx::float4>(isectRRect.rect());
286 auto rectToLocalSize = (local - skvx::shuffle<2,3,0,1>(local)) /
287 (rect - skvx::shuffle<2,3,0,1>(rect));
288 fHeadInstance->fLocalRect = skvx::bit_pun<SkRect>((isect - rect) * rectToLocalSize + local);
289
290 // Update the round rect.
291 fHeadInstance->fRRect = isectRRect;
292 return ClipResult::kClippedGeometrically;
293 }
294
295 return ClipResult::kFail;
296}
297
Chris Dalton57ab06c2021-04-22 12:57:28 -0600298GrProcessorSet::Analysis FillRRectOp::finalize(const GrCaps& caps, const GrAppliedClip* clip,
299 GrClampType clampType) {
Chris Dalton3c636f02021-07-08 14:50:57 -0600300 SkASSERT(fInstanceCount == 1);
301 SkASSERT(fHeadInstance->fNext == nullptr);
Chris Dalton133944a2018-11-16 23:30:29 -0500302
Robert Phillips360ec182020-03-26 13:29:50 -0400303 bool isWideColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -0600304 auto analysis = fHelper.finalizeProcessors(caps, clip, clampType,
Chris Dalton3c636f02021-07-08 14:50:57 -0600305 GrProcessorAnalysisCoverage::kSingleChannel,
306 &fHeadInstance->fColor, &isWideColor);
Robert Phillips360ec182020-03-26 13:29:50 -0400307 if (isWideColor) {
308 fProcessorFlags |= ProcessorFlags::kWideColor;
Brian Osman5105d682019-02-13 16:06:14 -0500309 }
Chris Dalton133944a2018-11-16 23:30:29 -0500310 if (analysis.usesLocalCoords()) {
Robert Phillips360ec182020-03-26 13:29:50 -0400311 fProcessorFlags |= ProcessorFlags::kHasLocalCoords;
Chris Dalton133944a2018-11-16 23:30:29 -0500312 }
Chris Dalton4b62aed2019-01-15 11:53:00 -0700313 return analysis;
Chris Dalton133944a2018-11-16 23:30:29 -0500314}
315
Herb Derbye25c3002020-10-27 15:57:27 -0400316GrOp::CombineResult FillRRectOp::onCombineIfPossible(GrOp* op, SkArenaAlloc*, const GrCaps& caps) {
Robert Phillips366176b2020-02-26 11:40:50 -0500317 const auto& that = *op->cast<FillRRectOp>();
Chris Dalton3c636f02021-07-08 14:50:57 -0600318 if (!fHelper.isCompatible(that.fHelper, caps, this->bounds(), that.bounds()) ||
319 fProcessorFlags != that.fProcessorFlags) {
Robert Phillips360ec182020-03-26 13:29:50 -0400320 return CombineResult::kCannotCombine;
321 }
322
Chris Dalton3c636f02021-07-08 14:50:57 -0600323 *fTailInstance = that.fHeadInstance;
324 fTailInstance = that.fTailInstance;
Chris Dalton133944a2018-11-16 23:30:29 -0500325 fInstanceCount += that.fInstanceCount;
Chris Dalton133944a2018-11-16 23:30:29 -0500326 return CombineResult::kMerged;
327}
328
Robert Phillips366176b2020-02-26 11:40:50 -0500329class FillRRectOp::Processor : public GrGeometryProcessor {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600330public:
Robert Phillips360ec182020-03-26 13:29:50 -0400331 static GrGeometryProcessor* Make(SkArenaAlloc* arena, GrAAType aaType, ProcessorFlags flags) {
Mike Kleinf1241082020-12-14 15:59:09 -0600332 return arena->make([&](void* ptr) {
333 return new (ptr) Processor(aaType, flags);
334 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500335 }
336
Robert Phillips8053c972019-11-21 10:44:53 -0500337 const char* name() const final { return "GrFillRRectOp::Processor"; }
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500338
Robert Phillips8053c972019-11-21 10:44:53 -0500339 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const final {
Chris Daltoncc13b352021-03-05 14:59:01 -0700340 b->addBits(kNumProcessorFlags, (uint32_t)fFlags, "flags");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500341 }
342
Robert Phillipsf10535f2021-03-23 09:30:45 -0400343 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500344
345private:
Robert Phillips360ec182020-03-26 13:29:50 -0400346 Processor(GrAAType aaType, ProcessorFlags flags)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500347 : INHERITED(kGrFillRRectOp_Processor_ClassID)
Chris Dalton0dffbab2019-03-27 13:08:50 -0600348 , fFlags(flags) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700349 this->setVertexAttributes(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
Chris Dalton133944a2018-11-16 23:30:29 -0500350
Chris Daltoncc13b352021-03-05 14:59:01 -0700351 fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
352 fInstanceAttribs.emplace_back("translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600353 fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
354 fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
355 fColorAttrib = &fInstanceAttribs.push_back(
Robert Phillips360ec182020-03-26 13:29:50 -0400356 MakeColorAttribute("color", (fFlags & ProcessorFlags::kWideColor)));
357 if (fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600358 fInstanceAttribs.emplace_back(
359 "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
360 }
Chris Dalton3c636f02021-07-08 14:50:57 -0600361 SkASSERT(fInstanceAttribs.count() <= kMaxInstanceAttribs);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600362 this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
Chris Dalton0dffbab2019-03-27 13:08:50 -0600363 }
364
Chris Dalton0dffbab2019-03-27 13:08:50 -0600365 static constexpr Attribute kVertexAttribs[] = {
366 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
367 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
368 // Coverage only.
369 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
370
Robert Phillips360ec182020-03-26 13:29:50 -0400371 const ProcessorFlags fFlags;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600372
Chris Dalton3c636f02021-07-08 14:50:57 -0600373 constexpr static int kMaxInstanceAttribs = 6;
374 SkSTArray<kMaxInstanceAttribs, Attribute> fInstanceAttribs;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600375 const Attribute* fColorAttrib;
376
Chris Daltoncc13b352021-03-05 14:59:01 -0700377 class Impl;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500378
John Stiles7571f9e2020-09-02 22:42:33 -0400379 using INHERITED = GrGeometryProcessor;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600380};
381
Robert Phillips787fd9d2021-03-22 14:48:09 -0400382constexpr GrGeometryProcessor::Attribute FillRRectOp::Processor::kVertexAttribs[];
Chris Dalton0dffbab2019-03-27 13:08:50 -0600383
384// Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
Chris Dalton133944a2018-11-16 23:30:29 -0500385// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
386// edges. The Vertex struct tells the shader where to place its vertex within a normalized
387// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600388struct CoverageVertex {
Chris Dalton133944a2018-11-16 23:30:29 -0500389 std::array<float, 4> fRadiiSelector;
390 std::array<float, 2> fCorner;
391 std::array<float, 2> fRadiusOutset;
392 std::array<float, 2> fAABloatDirection;
393 float fCoverage;
394 float fIsLinearCoverage;
Chris Dalton133944a2018-11-16 23:30:29 -0500395};
396
397// This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
398// of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
399// rectangles.
400static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
401
Chris Daltoncc13b352021-03-05 14:59:01 -0700402static constexpr CoverageVertex kVertexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500403 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700404 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
405 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500406
407 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700408 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
409 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500410
411 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700412 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
413 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500414
415 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700416 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
417 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500418
419
420 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700421 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
422 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500423
424 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700425 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
426 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500427
428 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700429 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
430 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500431
432 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700433 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
434 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500435
436
437 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700438 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
439 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
440 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
441 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
442 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
443 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500444
445 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700446 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
447 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
448 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
449 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
450 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
451 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500452
453 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700454 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
455 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
456 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
457 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
458 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
459 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500460
461 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700462 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
463 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
464 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
465 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
Chris Dalton2d07e862018-11-26 12:30:47 -0700466 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700467 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500468
Chris Daltoncc13b352021-03-05 14:59:01 -0700469GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500470
Chris Daltoncc13b352021-03-05 14:59:01 -0700471static constexpr uint16_t kIndexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500472 // Inset octagon (solid coverage).
473 0, 1, 7,
474 1, 2, 7,
475 7, 2, 6,
476 2, 3, 6,
477 6, 3, 5,
478 3, 4, 5,
479
480 // AA borders (linear coverage).
481 0, 1, 8, 1, 9, 8,
482 2, 3, 10, 3, 11, 10,
483 4, 5, 12, 5, 13, 12,
484 6, 7, 14, 7, 15, 14,
485
486 // Top-left arc.
487 16, 17, 21,
488 17, 21, 18,
489 21, 18, 20,
490 18, 20, 19,
491
492 // Top-right arc.
493 22, 23, 27,
494 23, 27, 24,
495 27, 24, 26,
496 24, 26, 25,
497
498 // Bottom-right arc.
499 28, 29, 33,
500 29, 33, 30,
501 33, 30, 32,
502 30, 32, 31,
503
504 // Bottom-left arc.
505 34, 35, 39,
506 35, 39, 36,
507 39, 36, 38,
508 36, 38, 37};
509
Chris Daltoncc13b352021-03-05 14:59:01 -0700510GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400511
Robert Phillips71143952021-06-17 14:55:07 -0400512void FillRRectOp::onPrepareDraws(GrMeshDrawTarget* target) {
Chris Dalton3c636f02021-07-08 14:50:57 -0600513 if (!fProgramInfo) {
514 this->createProgramInfo(target);
515 }
516
Chris Daltoncc29a392021-07-12 15:16:29 -0600517 // FIXME(skbug.com/12201): Our draw's MSAA state should match the render target, but DDL doesn't
518 // yet communicate DMSAA state to onPrePrepare.
519 SkASSERT(fProgramInfo->pipeline().isHWAntialiasState() == target->usesMSAASurface());
520
Chris Dalton3c636f02021-07-08 14:50:57 -0600521 size_t instanceStride = fProgramInfo->geomProc().instanceStride();
522
523 if (GrVertexWriter instanceWrter = target->makeVertexSpace(instanceStride, fInstanceCount,
524 &fInstanceBuffer, &fBaseInstance)) {
525 SkDEBUGCODE(auto end = instanceWrter.makeOffset(instanceStride * fInstanceCount));
526 for (Instance* i = fHeadInstance; i; i = i->fNext) {
527 auto [l, t, r, b] = i->fRRect.rect();
528
529 // Produce a matrix that draws the round rect from normalized [-1, -1, +1, +1] space.
530 SkMatrix m;
531 // Unmap the normalized rect [-1, -1, +1, +1] back to [l, t, r, b].
532 m.setScaleTranslate((r - l)/2, (b - t)/2, (l + r)/2, (t + b)/2);
533 // Map to device space.
534 m.postConcat(i->fViewMatrix);
535
536 // Convert the radii to [-1, -1, +1, +1] space and write their attribs.
537 grvx::float4 radiiX, radiiY;
538 grvx::strided_load2(&SkRRectPriv::GetRadiiArray(i->fRRect)->fX, radiiX, radiiY);
539 radiiX *= 2 / (r - l);
540 radiiY *= 2 / (b - t);
541
542 instanceWrter.write(
543 m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY(),
544 m.getTranslateX(), m.getTranslateY(),
545 radiiX,
546 radiiY,
547 GrVertexColor(i->fColor, fProcessorFlags & ProcessorFlags::kWideColor),
548 GrVertexWriter::If(fProcessorFlags & ProcessorFlags::kHasLocalCoords,
549 i->fLocalRect));
550 }
551 SkASSERT(instanceWrter == end);
Greg Danielf793de12019-09-05 13:23:23 -0400552 }
553
Chris Daltoncc13b352021-03-05 14:59:01 -0700554 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400555
Chris Daltoncc13b352021-03-05 14:59:01 -0700556 fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kIndex,
557 sizeof(kIndexData),
558 kIndexData, gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400559
Chris Daltoncc13b352021-03-05 14:59:01 -0700560 GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400561
Chris Daltoncc13b352021-03-05 14:59:01 -0700562 fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,
563 sizeof(kVertexData),
564 kVertexData,
565 gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400566}
567
Chris Daltoncc13b352021-03-05 14:59:01 -0700568class FillRRectOp::Processor::Impl : public GrGLSLGeometryProcessor {
Chris Dalton133944a2018-11-16 23:30:29 -0500569 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
John Stiles4d7ac492021-03-09 20:16:43 -0500570 GrGLSLVertexBuilder* v = args.fVertBuilder;
571 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
572
Robert Phillips787fd9d2021-03-22 14:48:09 -0400573 const auto& proc = args.fGeomProc.cast<Processor>();
Robert Phillips360ec182020-03-26 13:29:50 -0400574 bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
Chris Dalton133944a2018-11-16 23:30:29 -0500575
Chris Dalton0dffbab2019-03-27 13:08:50 -0600576 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
577
Chris Dalton133944a2018-11-16 23:30:29 -0500578 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
579 varyings->emitAttributes(proc);
John Stiles4d7ac492021-03-09 20:16:43 -0500580 f->codeAppendf("half4 %s;", args.fOutputColor);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600581 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500582 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
583
584 // Emit the vertex shader.
Chris Daltoncc13b352021-03-05 14:59:01 -0700585 // When MSAA is enabled, we need to make sure every sample gets lit up on pixels that have
586 // fractional coverage. We do this by making the ramp wider.
587 v->codeAppendf("float aa_bloat_multiplier = %i;",
588 (proc.fFlags & ProcessorFlags::kMSAAEnabled)
589 ? 2 // Outset an entire pixel (2 radii).
590 : (!(proc.fFlags & ProcessorFlags::kFakeNonAA))
591 ? 1 // Outset one half pixel (1 radius).
592 : 0); // No AA bloat.
593
Chris Dalton133944a2018-11-16 23:30:29 -0500594 // Unpack vertex attribs.
595 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
596 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
597 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
Chris Dalton133944a2018-11-16 23:30:29 -0500598 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
599
600 // Find the amount to bloat each edge for AA (in source space).
601 v->codeAppend("float2 pixellength = inversesqrt("
602 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
603 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
604 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
605 "abs(normalized_axis_dirs.zw));");
606 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
607
608 // Identify our radii.
Mike Reedd3efa992018-11-28 13:13:15 +0000609 v->codeAppend("float4 radii_and_neighbors = radii_selector"
610 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
611 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
612 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500613
Chris Daltoncc13b352021-03-05 14:59:01 -0700614 v->codeAppend("float coverage_multiplier = 1;");
Chris Dalton133944a2018-11-16 23:30:29 -0500615 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700616 // The rrect is more narrow than a half-pixel AA coverage ramp. We can't
617 // draw as-is or else opposite AA borders will overlap. Instead, fudge the
618 // size up to the width of a coverage ramp, and then reduce total coverage
619 // to make the rect appear more thin.
Chris Dalton133944a2018-11-16 23:30:29 -0500620 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
Chris Daltoncc13b352021-03-05 14:59:01 -0700621 v->codeAppend( "coverage_multiplier = 1 / (max(aa_bloatradius.x, 1) * "
622 "max(aa_bloatradius.y, 1));");
Chris Dalton133944a2018-11-16 23:30:29 -0500623 // Set radii to zero to ensure we take the "linear coverage" codepath.
624 // (The "coverage" variable only has effect in the linear codepath.)
625 v->codeAppend( "radii = float2(0);");
626 v->codeAppend("}");
627
Chris Daltoncc13b352021-03-05 14:59:01 -0700628 // Unpack coverage.
629 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
630 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
631 // MSAA has a wider ramp that goes from -.5 to 1.5 instead of 0 to 1.
632 v->codeAppendf("coverage = (coverage - .5) * aa_bloat_multiplier + .5;");
633 }
634
635 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.5))) {");
Chris Dalton133944a2018-11-16 23:30:29 -0500636 // The radii are very small. Demote this arc to a sharp 90 degree corner.
Chris Daltoncc13b352021-03-05 14:59:01 -0700637 v->codeAppend( "radii = float2(0);");
638 // Convert to a standard picture frame for an AA rect instead of the round
639 // rect geometry.
640 v->codeAppend( "aa_bloat_direction = sign(corner);");
641 v->codeAppend( "if (coverage > .5) {"); // Are we an inset edge?
642 v->codeAppend( "aa_bloat_direction = -aa_bloat_direction;");
643 v->codeAppend( "}");
Chris Dalton133944a2018-11-16 23:30:29 -0500644 v->codeAppend( "is_linear_coverage = 1;");
645 v->codeAppend("} else {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700646 // Don't let radii get smaller than a coverage ramp plus an extra half
647 // pixel for MSAA. Always use the same amount so we don't pop when
648 // switching between MSAA and coverage.
649 v->codeAppend( "radii = clamp(radii, pixellength * 1.5, 2 - pixellength * 1.5);");
650 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength * 1.5, "
651 "2 - pixellength * 1.5);");
Mike Reedd3efa992018-11-28 13:13:15 +0000652 // Don't let neighboring radii get closer together than 1/16 pixel.
653 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
654 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
655 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500656 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500657
658 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
659 // normalized [-1,-1,+1,+1] space.
Chris Daltoncc13b352021-03-05 14:59:01 -0700660 v->codeAppend("float2 aa_outset = "
661 "aa_bloat_direction * aa_bloatradius * aa_bloat_multiplier;");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700662 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500663
Chris Daltoncc13b352021-03-05 14:59:01 -0700664 v->codeAppend("if (coverage > .5) {"); // Are we an inset edge?
665 // Don't allow the aa insets to overlap. i.e., Don't let them inset past
666 // the center (x=y=0). Since we don't allow the rect to become thinner
667 // than 1px, this should only happen when using MSAA, where we inset by an
668 // entire pixel instead of half.
669 v->codeAppend( "if (aa_bloat_direction.x != 0 && vertexpos.x * corner.x < 0) {");
670 v->codeAppend( "float backset = abs(vertexpos.x);");
671 v->codeAppend( "vertexpos.x = 0;");
672 v->codeAppend( "vertexpos.y += "
673 "backset * sign(corner.y) * pixellength.y/pixellength.x;");
674 v->codeAppend( "coverage = (coverage - .5) * abs(corner.x) / "
675 "(abs(corner.x) + backset) + .5;");
676 v->codeAppend( "}");
677 v->codeAppend( "if (aa_bloat_direction.y != 0 && vertexpos.y * corner.y < 0) {");
678 v->codeAppend( "float backset = abs(vertexpos.y);");
679 v->codeAppend( "vertexpos.y = 0;");
680 v->codeAppend( "vertexpos.x += "
681 "backset * sign(corner.x) * pixellength.x/pixellength.y;");
682 v->codeAppend( "coverage = (coverage - .5) * abs(corner.y) / "
683 "(abs(corner.y) + backset) + .5;");
684 v->codeAppend( "}");
685 v->codeAppend("}");
686
Michael Ludwig553db622020-06-19 10:47:30 -0400687 // Write positions
Chris Dalton133944a2018-11-16 23:30:29 -0500688 GrShaderVar localCoord("", kFloat2_GrSLType);
Robert Phillips360ec182020-03-26 13:29:50 -0400689 if (proc.fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton133944a2018-11-16 23:30:29 -0500690 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
691 "local_rect.zw * (1 + vertexpos)) * .5;");
Michael Ludwig553db622020-06-19 10:47:30 -0400692 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
Chris Dalton133944a2018-11-16 23:30:29 -0500693 }
Chris Dalton133944a2018-11-16 23:30:29 -0500694
695 // Transform to device space.
696 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
697 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
698 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
699
700 // Setup interpolants for coverage.
701 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
702 varyings->addVarying("arccoord", &arcCoord);
703 v->codeAppend("if (0 != is_linear_coverage) {");
704 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
705 // interpolate linear coverage across y.
Chris Daltoncc13b352021-03-05 14:59:01 -0700706 v->codeAppendf( "%s.xy = float2(0, coverage * coverage_multiplier);",
707 arcCoord.vsOut());
Chris Dalton133944a2018-11-16 23:30:29 -0500708 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700709 // Find the normalized arc coordinates for our corner ellipse.
710 // (i.e., the coordinate system where x^2 + y^2 == 1).
711 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500712 // We are a corner piece: Interpolate the arc coordinates for coverage.
713 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
714 // instructs the fragment shader to use linear coverage).
715 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
716 if (!useHWDerivatives) {
717 // The gradient is order-1: Interpolate it across arccoord.zw.
718 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
719 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
720 }
721 v->codeAppend("}");
722
723 // Emit the fragment shader.
Chris Dalton133944a2018-11-16 23:30:29 -0500724 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
725 f->codeAppendf("half coverage;");
726 f->codeAppendf("if (0 == x_plus_1) {");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600727 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500728 f->codeAppendf("} else {");
729 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
730 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
731 if (useHWDerivatives) {
732 f->codeAppendf("float fnwidth = fwidth(fn);");
733 } else {
734 // The gradient is interpolated across arccoord.zw.
735 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
736 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
737 }
Chris Daltoncc13b352021-03-05 14:59:01 -0700738 f->codeAppendf( "coverage = .5 - half(fn/fnwidth);");
739 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
740 // MSAA uses ramps larger than 1px, so we need to clamp in both branches.
741 f->codeAppendf("}");
742 }
743 f->codeAppendf("coverage = clamp(coverage, 0, 1);");
744 if (!(proc.fFlags & ProcessorFlags::kMSAAEnabled)) {
745 // When not using MSAA, we only need to clamp in the "arc" branch.
746 f->codeAppendf("}");
747 }
748 if (proc.fFlags & ProcessorFlags::kFakeNonAA) {
749 f->codeAppendf("coverage = (coverage >= .5) ? 1 : 0;");
750 }
John Stiles4d7ac492021-03-09 20:16:43 -0500751 f->codeAppendf("half4 %s = half4(coverage);", args.fOutputCoverage);
Chris Dalton133944a2018-11-16 23:30:29 -0500752 }
753
Brian Salomon5a328282021-04-14 10:32:25 -0400754 void setData(const GrGLSLProgramDataManager&,
755 const GrShaderCaps&,
756 const GrGeometryProcessor&) override {}
Chris Dalton133944a2018-11-16 23:30:29 -0500757};
758
Chris Dalton0dffbab2019-03-27 13:08:50 -0600759
Robert Phillipsf10535f2021-03-23 09:30:45 -0400760GrGLSLGeometryProcessor* FillRRectOp::Processor::createGLSLInstance(const GrShaderCaps&) const {
Chris Daltoncc13b352021-03-05 14:59:01 -0700761 return new Impl();
Chris Dalton133944a2018-11-16 23:30:29 -0500762}
763
Robert Phillipscad8fba2020-03-20 15:39:29 -0400764void FillRRectOp::onCreateProgramInfo(const GrCaps* caps,
765 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -0500766 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -0600767 bool usesMSAASurface,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400768 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -0400769 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500770 GrXferBarrierFlags renderPassXferBarriers,
771 GrLoadOp colorLoadOp) {
Chris Dalton6aaf00f2021-07-13 13:26:39 -0600772 GrPipeline::InputFlags pipelineFlags = fHelper.pipelineFlags();
773 if (usesMSAASurface) {
774 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
Chris Daltoncc29a392021-07-12 15:16:29 -0600775 fProcessorFlags |= ProcessorFlags::kMSAAEnabled;
776 }
Robert Phillips360ec182020-03-26 13:29:50 -0400777 GrGeometryProcessor* gp = Processor::Make(arena, fHelper.aaType(), fProcessorFlags);
Chris Daltoncc29a392021-07-12 15:16:29 -0600778 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(
779 caps, arena, writeView, std::move(appliedClip), dstProxyView, gp,
780 fHelper.detachProcessorSet(), GrPrimitiveType::kTriangles, renderPassXferBarriers,
Chris Dalton6aaf00f2021-07-13 13:26:39 -0600781 colorLoadOp, pipelineFlags);
Robert Phillips8053c972019-11-21 10:44:53 -0500782}
783
Robert Phillips366176b2020-02-26 11:40:50 -0500784void FillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Robert Phillips8053c972019-11-21 10:44:53 -0500785 if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
786 return; // Setup failed.
787 }
788
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600789 flushState->bindPipelineAndScissorClip(*fProgramInfo, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400790 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Greg Daniel426274b2020-07-20 11:37:38 -0400791 flushState->bindBuffers(std::move(fIndexBuffer), std::move(fInstanceBuffer),
792 std::move(fVertexBuffer));
Chris Daltoncc13b352021-03-05 14:59:01 -0700793 flushState->drawIndexedInstanced(SK_ARRAY_COUNT(kIndexData), 0, fInstanceCount, fBaseInstance,
794 0);
Chris Dalton133944a2018-11-16 23:30:29 -0500795}
796
797// Will the given corner look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600798static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
Chris Dalton133944a2018-11-16 23:30:29 -0500799 Sk2f devRadii = devScale * cornerRadii;
800 if (devRadii[1] < devRadii[0]) {
801 devRadii = SkNx_shuffle<1,0>(devRadii);
802 }
Brian Osman788b9162020-02-07 10:36:46 -0500803 float minDevRadius = std::max(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
Chris Dalton133944a2018-11-16 23:30:29 -0500804 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
805 // This threshold was arrived at subjevtively on an NVIDIA chip.
806 return minDevRadius * minDevRadius * 5 > devRadii[1];
807}
808
Chris Dalton0dffbab2019-03-27 13:08:50 -0600809static bool can_use_hw_derivatives_with_coverage(
810 const Sk2f& devScale, const SkVector& cornerRadii) {
811 return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
Chris Dalton133944a2018-11-16 23:30:29 -0500812}
813
814// Will the given round rect look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600815static bool can_use_hw_derivatives_with_coverage(
816 const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
Chris Dalton133944a2018-11-16 23:30:29 -0500817 if (!shaderCaps.shaderDerivativeSupport()) {
818 return false;
819 }
820
821 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
822 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
823 Sk2f devScale = (x*x + y*y).sqrt();
824 switch (rrect.getType()) {
825 case SkRRect::kEmpty_Type:
826 case SkRRect::kRect_Type:
827 return true;
828
829 case SkRRect::kOval_Type:
830 case SkRRect::kSimple_Type:
Chris Dalton0dffbab2019-03-27 13:08:50 -0600831 return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
Chris Dalton133944a2018-11-16 23:30:29 -0500832
833 case SkRRect::kNinePatch_Type: {
834 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
835 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
836 Sk2f minRadii = Sk2f::Min(r0, r1);
837 Sk2f maxRadii = Sk2f::Max(r0, r1);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600838 return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
839 can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
Chris Dalton133944a2018-11-16 23:30:29 -0500840 }
841
842 case SkRRect::kComplex_Type: {
843 for (int i = 0; i < 4; ++i) {
844 auto corner = static_cast<SkRRect::Corner>(i);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600845 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
Chris Dalton133944a2018-11-16 23:30:29 -0500846 return false;
847 }
848 }
849 return true;
850 }
851 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600852 SK_ABORT("Invalid round rect type.");
Chris Dalton133944a2018-11-16 23:30:29 -0500853}
Robert Phillips366176b2020-02-26 11:40:50 -0500854
855} // anonymous namespace
856
857
Herb Derbyc76d4092020-10-07 16:46:15 -0400858GrOp::Owner GrFillRRectOp::Make(GrRecordingContext* ctx,
Chris Dalton3c636f02021-07-08 14:50:57 -0600859 SkArenaAlloc* arena,
Herb Derbyc76d4092020-10-07 16:46:15 -0400860 GrPaint&& paint,
861 const SkMatrix& viewMatrix,
862 const SkRRect& rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600863 const SkRect& localRect,
Chris Dalton4f447342021-03-12 12:09:12 -0700864 GrAA aa) {
Chris Dalton3c636f02021-07-08 14:50:57 -0600865 return FillRRectOp::Make(ctx, arena, std::move(paint), viewMatrix, rrect, localRect, aa);
Robert Phillips366176b2020-02-26 11:40:50 -0500866}
867
Chris Dalton61d694d2021-03-22 00:00:52 -0600868
Robert Phillips366176b2020-02-26 11:40:50 -0500869#if GR_TEST_UTILS
870
871#include "src/gpu/GrDrawOpTest.h"
872
873GR_DRAW_OP_TEST_DEFINE(FillRRectOp) {
Chris Dalton3c636f02021-07-08 14:50:57 -0600874 SkArenaAlloc arena(64 * sizeof(float));
Robert Phillips366176b2020-02-26 11:40:50 -0500875 SkMatrix viewMatrix = GrTest::TestMatrix(random);
Chris Dalton4f447342021-03-12 12:09:12 -0700876 GrAA aa = GrAA(random->nextBool());
Robert Phillips366176b2020-02-26 11:40:50 -0500877
878 SkRect rect = GrTest::TestRect(random);
879 float w = rect.width();
880 float h = rect.height();
881
882 SkRRect rrect;
883 // TODO: test out other rrect configurations
884 rrect.setNinePatch(rect, w / 3.0f, h / 4.0f, w / 5.0f, h / 6.0);
885
886 return GrFillRRectOp::Make(context,
Chris Dalton3c636f02021-07-08 14:50:57 -0600887 &arena,
Robert Phillips360ec182020-03-26 13:29:50 -0400888 std::move(paint),
Robert Phillips366176b2020-02-26 11:40:50 -0500889 viewMatrix,
890 rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600891 rrect.rect(),
Chris Dalton4f447342021-03-12 12:09:12 -0700892 aa);
Robert Phillips366176b2020-02-26 11:40:50 -0500893}
894
895#endif