blob: e9b5493c7e2e35284f935598f2438cab52fb3f1e [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
Michael Ludwig575c9212021-07-13 11:09:52 -0400274 // Don't apply the clip geometrically if it becomes subpixel, since then the hairline
275 // rendering may outset beyond the original clip.
276 SkRect devISectBounds = fHeadInstance->fViewMatrix.mapRect(isectRRect.rect());
277 if (devISectBounds.width() < 1.f || devISectBounds.height() < 1.f) {
278 return ClipResult::kFail;
279 }
280
Chris Daltonea46ef32021-07-12 15:09:06 -0600281 // Update the local rect.
282 auto rect = skvx::bit_pun<grvx::float4>(fHeadInstance->fRRect.rect());
283 auto local = skvx::bit_pun<grvx::float4>(fHeadInstance->fLocalRect);
284 auto isect = skvx::bit_pun<grvx::float4>(isectRRect.rect());
285 auto rectToLocalSize = (local - skvx::shuffle<2,3,0,1>(local)) /
286 (rect - skvx::shuffle<2,3,0,1>(rect));
287 fHeadInstance->fLocalRect = skvx::bit_pun<SkRect>((isect - rect) * rectToLocalSize + local);
288
289 // Update the round rect.
290 fHeadInstance->fRRect = isectRRect;
291 return ClipResult::kClippedGeometrically;
292 }
293
294 return ClipResult::kFail;
295}
296
Chris Dalton57ab06c2021-04-22 12:57:28 -0600297GrProcessorSet::Analysis FillRRectOp::finalize(const GrCaps& caps, const GrAppliedClip* clip,
298 GrClampType clampType) {
Chris Dalton3c636f02021-07-08 14:50:57 -0600299 SkASSERT(fInstanceCount == 1);
300 SkASSERT(fHeadInstance->fNext == nullptr);
Chris Dalton133944a2018-11-16 23:30:29 -0500301
Robert Phillips360ec182020-03-26 13:29:50 -0400302 bool isWideColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -0600303 auto analysis = fHelper.finalizeProcessors(caps, clip, clampType,
Chris Dalton3c636f02021-07-08 14:50:57 -0600304 GrProcessorAnalysisCoverage::kSingleChannel,
305 &fHeadInstance->fColor, &isWideColor);
Robert Phillips360ec182020-03-26 13:29:50 -0400306 if (isWideColor) {
307 fProcessorFlags |= ProcessorFlags::kWideColor;
Brian Osman5105d682019-02-13 16:06:14 -0500308 }
Chris Dalton133944a2018-11-16 23:30:29 -0500309 if (analysis.usesLocalCoords()) {
Robert Phillips360ec182020-03-26 13:29:50 -0400310 fProcessorFlags |= ProcessorFlags::kHasLocalCoords;
Chris Dalton133944a2018-11-16 23:30:29 -0500311 }
Chris Dalton4b62aed2019-01-15 11:53:00 -0700312 return analysis;
Chris Dalton133944a2018-11-16 23:30:29 -0500313}
314
Herb Derbye25c3002020-10-27 15:57:27 -0400315GrOp::CombineResult FillRRectOp::onCombineIfPossible(GrOp* op, SkArenaAlloc*, const GrCaps& caps) {
Robert Phillips366176b2020-02-26 11:40:50 -0500316 const auto& that = *op->cast<FillRRectOp>();
Chris Dalton3c636f02021-07-08 14:50:57 -0600317 if (!fHelper.isCompatible(that.fHelper, caps, this->bounds(), that.bounds()) ||
318 fProcessorFlags != that.fProcessorFlags) {
Robert Phillips360ec182020-03-26 13:29:50 -0400319 return CombineResult::kCannotCombine;
320 }
321
Chris Dalton3c636f02021-07-08 14:50:57 -0600322 *fTailInstance = that.fHeadInstance;
323 fTailInstance = that.fTailInstance;
Chris Dalton133944a2018-11-16 23:30:29 -0500324 fInstanceCount += that.fInstanceCount;
Chris Dalton133944a2018-11-16 23:30:29 -0500325 return CombineResult::kMerged;
326}
327
Robert Phillips366176b2020-02-26 11:40:50 -0500328class FillRRectOp::Processor : public GrGeometryProcessor {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600329public:
Robert Phillips360ec182020-03-26 13:29:50 -0400330 static GrGeometryProcessor* Make(SkArenaAlloc* arena, GrAAType aaType, ProcessorFlags flags) {
Mike Kleinf1241082020-12-14 15:59:09 -0600331 return arena->make([&](void* ptr) {
332 return new (ptr) Processor(aaType, flags);
333 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500334 }
335
Robert Phillips8053c972019-11-21 10:44:53 -0500336 const char* name() const final { return "GrFillRRectOp::Processor"; }
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500337
Robert Phillips8053c972019-11-21 10:44:53 -0500338 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const final {
Chris Daltoncc13b352021-03-05 14:59:01 -0700339 b->addBits(kNumProcessorFlags, (uint32_t)fFlags, "flags");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500340 }
341
Robert Phillipsf10535f2021-03-23 09:30:45 -0400342 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500343
344private:
Robert Phillips360ec182020-03-26 13:29:50 -0400345 Processor(GrAAType aaType, ProcessorFlags flags)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500346 : INHERITED(kGrFillRRectOp_Processor_ClassID)
Chris Dalton0dffbab2019-03-27 13:08:50 -0600347 , fFlags(flags) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700348 this->setVertexAttributes(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
Chris Dalton133944a2018-11-16 23:30:29 -0500349
Chris Daltoncc13b352021-03-05 14:59:01 -0700350 fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
351 fInstanceAttribs.emplace_back("translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600352 fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
353 fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
354 fColorAttrib = &fInstanceAttribs.push_back(
Robert Phillips360ec182020-03-26 13:29:50 -0400355 MakeColorAttribute("color", (fFlags & ProcessorFlags::kWideColor)));
356 if (fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600357 fInstanceAttribs.emplace_back(
358 "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
359 }
Chris Dalton3c636f02021-07-08 14:50:57 -0600360 SkASSERT(fInstanceAttribs.count() <= kMaxInstanceAttribs);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600361 this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
Chris Dalton0dffbab2019-03-27 13:08:50 -0600362 }
363
Chris Dalton0dffbab2019-03-27 13:08:50 -0600364 static constexpr Attribute kVertexAttribs[] = {
365 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
366 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
367 // Coverage only.
368 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
369
Robert Phillips360ec182020-03-26 13:29:50 -0400370 const ProcessorFlags fFlags;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600371
Chris Dalton3c636f02021-07-08 14:50:57 -0600372 constexpr static int kMaxInstanceAttribs = 6;
373 SkSTArray<kMaxInstanceAttribs, Attribute> fInstanceAttribs;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600374 const Attribute* fColorAttrib;
375
Chris Daltoncc13b352021-03-05 14:59:01 -0700376 class Impl;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500377
John Stiles7571f9e2020-09-02 22:42:33 -0400378 using INHERITED = GrGeometryProcessor;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600379};
380
Robert Phillips787fd9d2021-03-22 14:48:09 -0400381constexpr GrGeometryProcessor::Attribute FillRRectOp::Processor::kVertexAttribs[];
Chris Dalton0dffbab2019-03-27 13:08:50 -0600382
383// Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
Chris Dalton133944a2018-11-16 23:30:29 -0500384// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
385// edges. The Vertex struct tells the shader where to place its vertex within a normalized
386// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600387struct CoverageVertex {
Chris Dalton133944a2018-11-16 23:30:29 -0500388 std::array<float, 4> fRadiiSelector;
389 std::array<float, 2> fCorner;
390 std::array<float, 2> fRadiusOutset;
391 std::array<float, 2> fAABloatDirection;
392 float fCoverage;
393 float fIsLinearCoverage;
Chris Dalton133944a2018-11-16 23:30:29 -0500394};
395
396// This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
397// of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
398// rectangles.
399static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
400
Chris Daltoncc13b352021-03-05 14:59:01 -0700401static constexpr CoverageVertex kVertexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500402 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700403 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
404 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500405
406 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700407 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
408 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500409
410 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700411 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
412 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500413
414 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700415 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
416 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500417
418
419 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700420 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
421 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500422
423 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700424 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
425 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500426
427 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700428 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
429 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500430
431 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700432 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
433 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500434
435
436 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700437 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
438 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
439 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
440 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
441 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
442 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500443
444 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700445 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
446 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
447 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
448 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
449 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
450 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500451
452 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700453 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
454 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
455 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
456 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
457 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
458 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500459
460 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700461 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
462 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
463 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
464 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
Chris Dalton2d07e862018-11-26 12:30:47 -0700465 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700466 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500467
Chris Daltoncc13b352021-03-05 14:59:01 -0700468GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500469
Chris Daltoncc13b352021-03-05 14:59:01 -0700470static constexpr uint16_t kIndexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500471 // Inset octagon (solid coverage).
472 0, 1, 7,
473 1, 2, 7,
474 7, 2, 6,
475 2, 3, 6,
476 6, 3, 5,
477 3, 4, 5,
478
479 // AA borders (linear coverage).
480 0, 1, 8, 1, 9, 8,
481 2, 3, 10, 3, 11, 10,
482 4, 5, 12, 5, 13, 12,
483 6, 7, 14, 7, 15, 14,
484
485 // Top-left arc.
486 16, 17, 21,
487 17, 21, 18,
488 21, 18, 20,
489 18, 20, 19,
490
491 // Top-right arc.
492 22, 23, 27,
493 23, 27, 24,
494 27, 24, 26,
495 24, 26, 25,
496
497 // Bottom-right arc.
498 28, 29, 33,
499 29, 33, 30,
500 33, 30, 32,
501 30, 32, 31,
502
503 // Bottom-left arc.
504 34, 35, 39,
505 35, 39, 36,
506 39, 36, 38,
507 36, 38, 37};
508
Chris Daltoncc13b352021-03-05 14:59:01 -0700509GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400510
Robert Phillips71143952021-06-17 14:55:07 -0400511void FillRRectOp::onPrepareDraws(GrMeshDrawTarget* target) {
Chris Daltoncc29a392021-07-12 15:16:29 -0600512 if (target->usesMSAASurface()) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700513 fProcessorFlags |= ProcessorFlags::kMSAAEnabled;
514 }
515
Chris Dalton3c636f02021-07-08 14:50:57 -0600516 if (!fProgramInfo) {
517 this->createProgramInfo(target);
518 }
519
Chris Daltoncc29a392021-07-12 15:16:29 -0600520 // FIXME(skbug.com/12201): Our draw's MSAA state should match the render target, but DDL doesn't
521 // yet communicate DMSAA state to onPrePrepare.
522 SkASSERT(fProgramInfo->pipeline().isHWAntialiasState() == target->usesMSAASurface());
523
Chris Dalton3c636f02021-07-08 14:50:57 -0600524 size_t instanceStride = fProgramInfo->geomProc().instanceStride();
525
526 if (GrVertexWriter instanceWrter = target->makeVertexSpace(instanceStride, fInstanceCount,
527 &fInstanceBuffer, &fBaseInstance)) {
528 SkDEBUGCODE(auto end = instanceWrter.makeOffset(instanceStride * fInstanceCount));
529 for (Instance* i = fHeadInstance; i; i = i->fNext) {
530 auto [l, t, r, b] = i->fRRect.rect();
531
532 // Produce a matrix that draws the round rect from normalized [-1, -1, +1, +1] space.
533 SkMatrix m;
534 // Unmap the normalized rect [-1, -1, +1, +1] back to [l, t, r, b].
535 m.setScaleTranslate((r - l)/2, (b - t)/2, (l + r)/2, (t + b)/2);
536 // Map to device space.
537 m.postConcat(i->fViewMatrix);
538
539 // Convert the radii to [-1, -1, +1, +1] space and write their attribs.
540 grvx::float4 radiiX, radiiY;
541 grvx::strided_load2(&SkRRectPriv::GetRadiiArray(i->fRRect)->fX, radiiX, radiiY);
542 radiiX *= 2 / (r - l);
543 radiiY *= 2 / (b - t);
544
545 instanceWrter.write(
546 m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY(),
547 m.getTranslateX(), m.getTranslateY(),
548 radiiX,
549 radiiY,
550 GrVertexColor(i->fColor, fProcessorFlags & ProcessorFlags::kWideColor),
551 GrVertexWriter::If(fProcessorFlags & ProcessorFlags::kHasLocalCoords,
552 i->fLocalRect));
553 }
554 SkASSERT(instanceWrter == end);
Greg Danielf793de12019-09-05 13:23:23 -0400555 }
556
Chris Daltoncc13b352021-03-05 14:59:01 -0700557 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400558
Chris Daltoncc13b352021-03-05 14:59:01 -0700559 fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kIndex,
560 sizeof(kIndexData),
561 kIndexData, gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400562
Chris Daltoncc13b352021-03-05 14:59:01 -0700563 GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400564
Chris Daltoncc13b352021-03-05 14:59:01 -0700565 fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,
566 sizeof(kVertexData),
567 kVertexData,
568 gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400569}
570
Chris Daltoncc13b352021-03-05 14:59:01 -0700571class FillRRectOp::Processor::Impl : public GrGLSLGeometryProcessor {
Chris Dalton133944a2018-11-16 23:30:29 -0500572 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
John Stiles4d7ac492021-03-09 20:16:43 -0500573 GrGLSLVertexBuilder* v = args.fVertBuilder;
574 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
575
Robert Phillips787fd9d2021-03-22 14:48:09 -0400576 const auto& proc = args.fGeomProc.cast<Processor>();
Robert Phillips360ec182020-03-26 13:29:50 -0400577 bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
Chris Dalton133944a2018-11-16 23:30:29 -0500578
Chris Dalton0dffbab2019-03-27 13:08:50 -0600579 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
580
Chris Dalton133944a2018-11-16 23:30:29 -0500581 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
582 varyings->emitAttributes(proc);
John Stiles4d7ac492021-03-09 20:16:43 -0500583 f->codeAppendf("half4 %s;", args.fOutputColor);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600584 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500585 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
586
587 // Emit the vertex shader.
Chris Daltoncc13b352021-03-05 14:59:01 -0700588 // When MSAA is enabled, we need to make sure every sample gets lit up on pixels that have
589 // fractional coverage. We do this by making the ramp wider.
590 v->codeAppendf("float aa_bloat_multiplier = %i;",
591 (proc.fFlags & ProcessorFlags::kMSAAEnabled)
592 ? 2 // Outset an entire pixel (2 radii).
593 : (!(proc.fFlags & ProcessorFlags::kFakeNonAA))
594 ? 1 // Outset one half pixel (1 radius).
595 : 0); // No AA bloat.
596
Chris Dalton133944a2018-11-16 23:30:29 -0500597 // Unpack vertex attribs.
598 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
599 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
600 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
Chris Dalton133944a2018-11-16 23:30:29 -0500601 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
602
603 // Find the amount to bloat each edge for AA (in source space).
604 v->codeAppend("float2 pixellength = inversesqrt("
605 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
606 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
607 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
608 "abs(normalized_axis_dirs.zw));");
609 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
610
611 // Identify our radii.
Mike Reedd3efa992018-11-28 13:13:15 +0000612 v->codeAppend("float4 radii_and_neighbors = radii_selector"
613 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
614 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
615 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500616
Chris Daltoncc13b352021-03-05 14:59:01 -0700617 v->codeAppend("float coverage_multiplier = 1;");
Chris Dalton133944a2018-11-16 23:30:29 -0500618 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700619 // The rrect is more narrow than a half-pixel AA coverage ramp. We can't
620 // draw as-is or else opposite AA borders will overlap. Instead, fudge the
621 // size up to the width of a coverage ramp, and then reduce total coverage
622 // to make the rect appear more thin.
Chris Dalton133944a2018-11-16 23:30:29 -0500623 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
Chris Daltoncc13b352021-03-05 14:59:01 -0700624 v->codeAppend( "coverage_multiplier = 1 / (max(aa_bloatradius.x, 1) * "
625 "max(aa_bloatradius.y, 1));");
Chris Dalton133944a2018-11-16 23:30:29 -0500626 // Set radii to zero to ensure we take the "linear coverage" codepath.
627 // (The "coverage" variable only has effect in the linear codepath.)
628 v->codeAppend( "radii = float2(0);");
629 v->codeAppend("}");
630
Chris Daltoncc13b352021-03-05 14:59:01 -0700631 // Unpack coverage.
632 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
633 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
634 // MSAA has a wider ramp that goes from -.5 to 1.5 instead of 0 to 1.
635 v->codeAppendf("coverage = (coverage - .5) * aa_bloat_multiplier + .5;");
636 }
637
638 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.5))) {");
Chris Dalton133944a2018-11-16 23:30:29 -0500639 // The radii are very small. Demote this arc to a sharp 90 degree corner.
Chris Daltoncc13b352021-03-05 14:59:01 -0700640 v->codeAppend( "radii = float2(0);");
641 // Convert to a standard picture frame for an AA rect instead of the round
642 // rect geometry.
643 v->codeAppend( "aa_bloat_direction = sign(corner);");
644 v->codeAppend( "if (coverage > .5) {"); // Are we an inset edge?
645 v->codeAppend( "aa_bloat_direction = -aa_bloat_direction;");
646 v->codeAppend( "}");
Chris Dalton133944a2018-11-16 23:30:29 -0500647 v->codeAppend( "is_linear_coverage = 1;");
648 v->codeAppend("} else {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700649 // Don't let radii get smaller than a coverage ramp plus an extra half
650 // pixel for MSAA. Always use the same amount so we don't pop when
651 // switching between MSAA and coverage.
652 v->codeAppend( "radii = clamp(radii, pixellength * 1.5, 2 - pixellength * 1.5);");
653 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength * 1.5, "
654 "2 - pixellength * 1.5);");
Mike Reedd3efa992018-11-28 13:13:15 +0000655 // Don't let neighboring radii get closer together than 1/16 pixel.
656 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
657 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
658 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500659 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500660
661 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
662 // normalized [-1,-1,+1,+1] space.
Chris Daltoncc13b352021-03-05 14:59:01 -0700663 v->codeAppend("float2 aa_outset = "
664 "aa_bloat_direction * aa_bloatradius * aa_bloat_multiplier;");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700665 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500666
Chris Daltoncc13b352021-03-05 14:59:01 -0700667 v->codeAppend("if (coverage > .5) {"); // Are we an inset edge?
668 // Don't allow the aa insets to overlap. i.e., Don't let them inset past
669 // the center (x=y=0). Since we don't allow the rect to become thinner
670 // than 1px, this should only happen when using MSAA, where we inset by an
671 // entire pixel instead of half.
672 v->codeAppend( "if (aa_bloat_direction.x != 0 && vertexpos.x * corner.x < 0) {");
673 v->codeAppend( "float backset = abs(vertexpos.x);");
674 v->codeAppend( "vertexpos.x = 0;");
675 v->codeAppend( "vertexpos.y += "
676 "backset * sign(corner.y) * pixellength.y/pixellength.x;");
677 v->codeAppend( "coverage = (coverage - .5) * abs(corner.x) / "
678 "(abs(corner.x) + backset) + .5;");
679 v->codeAppend( "}");
680 v->codeAppend( "if (aa_bloat_direction.y != 0 && vertexpos.y * corner.y < 0) {");
681 v->codeAppend( "float backset = abs(vertexpos.y);");
682 v->codeAppend( "vertexpos.y = 0;");
683 v->codeAppend( "vertexpos.x += "
684 "backset * sign(corner.x) * pixellength.x/pixellength.y;");
685 v->codeAppend( "coverage = (coverage - .5) * abs(corner.y) / "
686 "(abs(corner.y) + backset) + .5;");
687 v->codeAppend( "}");
688 v->codeAppend("}");
689
Michael Ludwig553db622020-06-19 10:47:30 -0400690 // Write positions
Chris Dalton133944a2018-11-16 23:30:29 -0500691 GrShaderVar localCoord("", kFloat2_GrSLType);
Robert Phillips360ec182020-03-26 13:29:50 -0400692 if (proc.fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton133944a2018-11-16 23:30:29 -0500693 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
694 "local_rect.zw * (1 + vertexpos)) * .5;");
Michael Ludwig553db622020-06-19 10:47:30 -0400695 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
Chris Dalton133944a2018-11-16 23:30:29 -0500696 }
Chris Dalton133944a2018-11-16 23:30:29 -0500697
698 // Transform to device space.
699 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
700 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
701 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
702
703 // Setup interpolants for coverage.
704 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
705 varyings->addVarying("arccoord", &arcCoord);
706 v->codeAppend("if (0 != is_linear_coverage) {");
707 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
708 // interpolate linear coverage across y.
Chris Daltoncc13b352021-03-05 14:59:01 -0700709 v->codeAppendf( "%s.xy = float2(0, coverage * coverage_multiplier);",
710 arcCoord.vsOut());
Chris Dalton133944a2018-11-16 23:30:29 -0500711 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700712 // Find the normalized arc coordinates for our corner ellipse.
713 // (i.e., the coordinate system where x^2 + y^2 == 1).
714 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500715 // We are a corner piece: Interpolate the arc coordinates for coverage.
716 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
717 // instructs the fragment shader to use linear coverage).
718 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
719 if (!useHWDerivatives) {
720 // The gradient is order-1: Interpolate it across arccoord.zw.
721 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
722 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
723 }
724 v->codeAppend("}");
725
726 // Emit the fragment shader.
Chris Dalton133944a2018-11-16 23:30:29 -0500727 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
728 f->codeAppendf("half coverage;");
729 f->codeAppendf("if (0 == x_plus_1) {");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600730 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500731 f->codeAppendf("} else {");
732 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
733 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
734 if (useHWDerivatives) {
735 f->codeAppendf("float fnwidth = fwidth(fn);");
736 } else {
737 // The gradient is interpolated across arccoord.zw.
738 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
739 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
740 }
Chris Daltoncc13b352021-03-05 14:59:01 -0700741 f->codeAppendf( "coverage = .5 - half(fn/fnwidth);");
742 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
743 // MSAA uses ramps larger than 1px, so we need to clamp in both branches.
744 f->codeAppendf("}");
745 }
746 f->codeAppendf("coverage = clamp(coverage, 0, 1);");
747 if (!(proc.fFlags & ProcessorFlags::kMSAAEnabled)) {
748 // When not using MSAA, we only need to clamp in the "arc" branch.
749 f->codeAppendf("}");
750 }
751 if (proc.fFlags & ProcessorFlags::kFakeNonAA) {
752 f->codeAppendf("coverage = (coverage >= .5) ? 1 : 0;");
753 }
John Stiles4d7ac492021-03-09 20:16:43 -0500754 f->codeAppendf("half4 %s = half4(coverage);", args.fOutputCoverage);
Chris Dalton133944a2018-11-16 23:30:29 -0500755 }
756
Brian Salomon5a328282021-04-14 10:32:25 -0400757 void setData(const GrGLSLProgramDataManager&,
758 const GrShaderCaps&,
759 const GrGeometryProcessor&) override {}
Chris Dalton133944a2018-11-16 23:30:29 -0500760};
761
Chris Dalton0dffbab2019-03-27 13:08:50 -0600762
Robert Phillipsf10535f2021-03-23 09:30:45 -0400763GrGLSLGeometryProcessor* FillRRectOp::Processor::createGLSLInstance(const GrShaderCaps&) const {
Chris Daltoncc13b352021-03-05 14:59:01 -0700764 return new Impl();
Chris Dalton133944a2018-11-16 23:30:29 -0500765}
766
Robert Phillipscad8fba2020-03-20 15:39:29 -0400767void FillRRectOp::onCreateProgramInfo(const GrCaps* caps,
768 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -0500769 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400770 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -0400771 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500772 GrXferBarrierFlags renderPassXferBarriers,
773 GrLoadOp colorLoadOp) {
Chris Daltoncc29a392021-07-12 15:16:29 -0600774 if (writeView.asRenderTargetProxy()->numSamples() > 1) {
775 fProcessorFlags |= ProcessorFlags::kMSAAEnabled;
776 }
777 auto extraPipelineFlags = (fProcessorFlags & ProcessorFlags::kMSAAEnabled)
778 ? GrPipeline::InputFlags::kHWAntialias
779 : GrPipeline::InputFlags::kNone;
Robert Phillips360ec182020-03-26 13:29:50 -0400780 GrGeometryProcessor* gp = Processor::Make(arena, fHelper.aaType(), fProcessorFlags);
Chris Daltoncc29a392021-07-12 15:16:29 -0600781 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(
782 caps, arena, writeView, std::move(appliedClip), dstProxyView, gp,
783 fHelper.detachProcessorSet(), GrPrimitiveType::kTriangles, renderPassXferBarriers,
784 colorLoadOp, fHelper.pipelineFlags() | extraPipelineFlags);
Robert Phillips8053c972019-11-21 10:44:53 -0500785}
786
Robert Phillips366176b2020-02-26 11:40:50 -0500787void FillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Robert Phillips8053c972019-11-21 10:44:53 -0500788 if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
789 return; // Setup failed.
790 }
791
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600792 flushState->bindPipelineAndScissorClip(*fProgramInfo, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400793 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Greg Daniel426274b2020-07-20 11:37:38 -0400794 flushState->bindBuffers(std::move(fIndexBuffer), std::move(fInstanceBuffer),
795 std::move(fVertexBuffer));
Chris Daltoncc13b352021-03-05 14:59:01 -0700796 flushState->drawIndexedInstanced(SK_ARRAY_COUNT(kIndexData), 0, fInstanceCount, fBaseInstance,
797 0);
Chris Dalton133944a2018-11-16 23:30:29 -0500798}
799
800// Will the given corner look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600801static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
Chris Dalton133944a2018-11-16 23:30:29 -0500802 Sk2f devRadii = devScale * cornerRadii;
803 if (devRadii[1] < devRadii[0]) {
804 devRadii = SkNx_shuffle<1,0>(devRadii);
805 }
Brian Osman788b9162020-02-07 10:36:46 -0500806 float minDevRadius = std::max(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
Chris Dalton133944a2018-11-16 23:30:29 -0500807 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
808 // This threshold was arrived at subjevtively on an NVIDIA chip.
809 return minDevRadius * minDevRadius * 5 > devRadii[1];
810}
811
Chris Dalton0dffbab2019-03-27 13:08:50 -0600812static bool can_use_hw_derivatives_with_coverage(
813 const Sk2f& devScale, const SkVector& cornerRadii) {
814 return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
Chris Dalton133944a2018-11-16 23:30:29 -0500815}
816
817// Will the given round rect look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600818static bool can_use_hw_derivatives_with_coverage(
819 const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
Chris Dalton133944a2018-11-16 23:30:29 -0500820 if (!shaderCaps.shaderDerivativeSupport()) {
821 return false;
822 }
823
824 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
825 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
826 Sk2f devScale = (x*x + y*y).sqrt();
827 switch (rrect.getType()) {
828 case SkRRect::kEmpty_Type:
829 case SkRRect::kRect_Type:
830 return true;
831
832 case SkRRect::kOval_Type:
833 case SkRRect::kSimple_Type:
Chris Dalton0dffbab2019-03-27 13:08:50 -0600834 return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
Chris Dalton133944a2018-11-16 23:30:29 -0500835
836 case SkRRect::kNinePatch_Type: {
837 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
838 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
839 Sk2f minRadii = Sk2f::Min(r0, r1);
840 Sk2f maxRadii = Sk2f::Max(r0, r1);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600841 return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
842 can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
Chris Dalton133944a2018-11-16 23:30:29 -0500843 }
844
845 case SkRRect::kComplex_Type: {
846 for (int i = 0; i < 4; ++i) {
847 auto corner = static_cast<SkRRect::Corner>(i);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600848 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
Chris Dalton133944a2018-11-16 23:30:29 -0500849 return false;
850 }
851 }
852 return true;
853 }
854 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600855 SK_ABORT("Invalid round rect type.");
Chris Dalton133944a2018-11-16 23:30:29 -0500856}
Robert Phillips366176b2020-02-26 11:40:50 -0500857
858} // anonymous namespace
859
860
Herb Derbyc76d4092020-10-07 16:46:15 -0400861GrOp::Owner GrFillRRectOp::Make(GrRecordingContext* ctx,
Chris Dalton3c636f02021-07-08 14:50:57 -0600862 SkArenaAlloc* arena,
Herb Derbyc76d4092020-10-07 16:46:15 -0400863 GrPaint&& paint,
864 const SkMatrix& viewMatrix,
865 const SkRRect& rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600866 const SkRect& localRect,
Chris Dalton4f447342021-03-12 12:09:12 -0700867 GrAA aa) {
Chris Dalton3c636f02021-07-08 14:50:57 -0600868 return FillRRectOp::Make(ctx, arena, std::move(paint), viewMatrix, rrect, localRect, aa);
Robert Phillips366176b2020-02-26 11:40:50 -0500869}
870
Chris Dalton61d694d2021-03-22 00:00:52 -0600871
Robert Phillips366176b2020-02-26 11:40:50 -0500872#if GR_TEST_UTILS
873
874#include "src/gpu/GrDrawOpTest.h"
875
876GR_DRAW_OP_TEST_DEFINE(FillRRectOp) {
Chris Dalton3c636f02021-07-08 14:50:57 -0600877 SkArenaAlloc arena(64 * sizeof(float));
Robert Phillips366176b2020-02-26 11:40:50 -0500878 SkMatrix viewMatrix = GrTest::TestMatrix(random);
Chris Dalton4f447342021-03-12 12:09:12 -0700879 GrAA aa = GrAA(random->nextBool());
Robert Phillips366176b2020-02-26 11:40:50 -0500880
881 SkRect rect = GrTest::TestRect(random);
882 float w = rect.width();
883 float h = rect.height();
884
885 SkRRect rrect;
886 // TODO: test out other rrect configurations
887 rrect.setNinePatch(rect, w / 3.0f, h / 4.0f, w / 5.0f, h / 6.0);
888
889 return GrFillRRectOp::Make(context,
Chris Dalton3c636f02021-07-08 14:50:57 -0600890 &arena,
Robert Phillips360ec182020-03-26 13:29:50 -0400891 std::move(paint),
Robert Phillips366176b2020-02-26 11:40:50 -0500892 viewMatrix,
893 rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600894 rrect.rect(),
Chris Dalton4f447342021-03-12 12:09:12 -0700895 aa);
Robert Phillips366176b2020-02-26 11:40:50 -0500896}
897
898#endif