blob: 2b55c204019e675158e500d718276c0f39fddf3e [file] [log] [blame]
Chris Dalton133944a2018-11-16 23:30:29 -05001/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/gpu/ops/GrFillRRectOp.h"
Chris Dalton133944a2018-11-16 23:30:29 -05009
Robert Phillipsb7bfbc22020-07-01 12:55:01 -040010#include "include/gpu/GrRecordingContext.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "src/core/SkRRectPriv.h"
12#include "src/gpu/GrCaps.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "src/gpu/GrMemoryPool.h"
14#include "src/gpu/GrOpFlushState.h"
Greg Daniel2d41d0d2019-08-26 11:08:51 -040015#include "src/gpu/GrOpsRenderPass.h"
Robert Phillips901aff02019-10-08 12:32:56 -040016#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/gpu/GrRecordingContextPriv.h"
Robert Phillips1a82a4e2021-07-01 10:27:44 -040018#include "src/gpu/GrResourceProvider.h"
Chris Dalton3c636f02021-07-08 14:50:57 -060019#include "src/gpu/GrVertexWriter.h"
20#include "src/gpu/GrVx.h"
Chris Daltonea46ef32021-07-12 15:09:06 -060021#include "src/gpu/geometry/GrShape.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050022#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
23#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
24#include "src/gpu/glsl/GrGLSLVarying.h"
25#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
Robert Phillipscad8fba2020-03-20 15:39:29 -040026#include "src/gpu/ops/GrMeshDrawOp.h"
Robert Phillipsce978572020-02-28 11:56:44 -050027#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
Robert Phillips366176b2020-02-26 11:40:50 -050028
29namespace {
30
Robert Phillipscad8fba2020-03-20 15:39:29 -040031class FillRRectOp : public GrMeshDrawOp {
Robert Phillips360ec182020-03-26 13:29:50 -040032private:
33 using Helper = GrSimpleMeshDrawOpHelper;
34
Robert Phillips366176b2020-02-26 11:40:50 -050035public:
36 DEFINE_OP_CLASS_ID
37
Herb Derbyc76d4092020-10-07 16:46:15 -040038 static GrOp::Owner Make(GrRecordingContext*,
Chris Dalton3c636f02021-07-08 14:50:57 -060039 SkArenaAlloc*,
Herb Derbyc76d4092020-10-07 16:46:15 -040040 GrPaint&&,
41 const SkMatrix& viewMatrix,
42 const SkRRect&,
Chris Dalton61d694d2021-03-22 00:00:52 -060043 const SkRect& localRect,
Chris Dalton4f447342021-03-12 12:09:12 -070044 GrAA);
Robert Phillips366176b2020-02-26 11:40:50 -050045
46 const char* name() const final { return "GrFillRRectOp"; }
47
Robert Phillips360ec182020-03-26 13:29:50 -040048 FixedFunctionFlags fixedFunctionFlags() const final { return fHelper.fixedFunctionFlags(); }
49
Chris Daltonea46ef32021-07-12 15:09:06 -060050 ClipResult clipToShape(GrSurfaceDrawContext*, SkClipOp, const SkMatrix& clipMatrix,
51 const GrShape&, GrAA) override;
52
Chris Dalton57ab06c2021-04-22 12:57:28 -060053 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) final;
Herb Derbye25c3002020-10-27 15:57:27 -040054 CombineResult onCombineIfPossible(GrOp*, SkArenaAlloc*, const GrCaps&) final;
Robert Phillips360ec182020-03-26 13:29:50 -040055
Robert Phillips294723d2021-06-17 09:23:58 -040056 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips366176b2020-02-26 11:40:50 -050057 if (fProgramInfo) {
Robert Phillips294723d2021-06-17 09:23:58 -040058 fProgramInfo->visitFPProxies(func);
Robert Phillips366176b2020-02-26 11:40:50 -050059 } else {
Robert Phillips294723d2021-06-17 09:23:58 -040060 fHelper.visitProxies(func);
Robert Phillips366176b2020-02-26 11:40:50 -050061 }
62 }
63
Robert Phillips71143952021-06-17 14:55:07 -040064 void onPrepareDraws(GrMeshDrawTarget*) final;
Robert Phillips366176b2020-02-26 11:40:50 -050065
66 void onExecute(GrOpFlushState*, const SkRect& chainBounds) final;
67
68private:
Robert Phillips360ec182020-03-26 13:29:50 -040069 friend class ::GrSimpleMeshDrawOpHelper; // for access to ctor
Herb Derbyc76d4092020-10-07 16:46:15 -040070 friend class ::GrOp; // for access to ctor
Robert Phillips360ec182020-03-26 13:29:50 -040071
72 enum class ProcessorFlags {
Robert Phillips366176b2020-02-26 11:40:50 -050073 kNone = 0,
74 kUseHWDerivatives = 1 << 0,
Chris Daltoncc13b352021-03-05 14:59:01 -070075 kHasLocalCoords = 1 << 1,
76 kWideColor = 1 << 2,
77 kMSAAEnabled = 1 << 3,
78 kFakeNonAA = 1 << 4,
Robert Phillips366176b2020-02-26 11:40:50 -050079 };
Chris Daltoncc13b352021-03-05 14:59:01 -070080 constexpr static int kNumProcessorFlags = 5;
Robert Phillips366176b2020-02-26 11:40:50 -050081
Robert Phillips360ec182020-03-26 13:29:50 -040082 GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(ProcessorFlags);
Robert Phillips366176b2020-02-26 11:40:50 -050083
84 class Processor;
85
Herb Derbyc76d4092020-10-07 16:46:15 -040086 FillRRectOp(GrProcessorSet*,
Robert Phillips360ec182020-03-26 13:29:50 -040087 const SkPMColor4f& paintColor,
Chris Dalton3c636f02021-07-08 14:50:57 -060088 SkArenaAlloc*,
89 const SkMatrix& viewMatrix,
Robert Phillips360ec182020-03-26 13:29:50 -040090 const SkRRect&,
Chris Dalton61d694d2021-03-22 00:00:52 -060091 const SkRect& localRect,
Chris Dalton3c636f02021-07-08 14:50:57 -060092 ProcessorFlags);
Robert Phillips366176b2020-02-26 11:40:50 -050093
Robert Phillipscad8fba2020-03-20 15:39:29 -040094 GrProgramInfo* programInfo() final { return fProgramInfo; }
95
Robert Phillips366176b2020-02-26 11:40:50 -050096 // Create a GrProgramInfo object in the provided arena
Robert Phillipscad8fba2020-03-20 15:39:29 -040097 void onCreateProgramInfo(const GrCaps*,
98 SkArenaAlloc*,
Adlai Hollere2296f72020-11-19 13:41:26 -050099 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400100 GrAppliedClip&&,
John Stiles52cb1d02021-06-02 11:58:05 -0400101 const GrDstProxyView&,
Greg Daniel42dbca52020-11-20 10:22:43 -0500102 GrXferBarrierFlags renderPassXferBarriers,
103 GrLoadOp colorLoadOp) final;
Robert Phillips366176b2020-02-26 11:40:50 -0500104
Robert Phillips360ec182020-03-26 13:29:50 -0400105 Helper fHelper;
Robert Phillips360ec182020-03-26 13:29:50 -0400106 ProcessorFlags fProcessorFlags;
Robert Phillips366176b2020-02-26 11:40:50 -0500107
Chris Dalton3c636f02021-07-08 14:50:57 -0600108 struct Instance {
109 Instance(const SkMatrix& viewMatrix, const SkRRect& rrect, const SkRect& localRect,
110 const SkPMColor4f& color)
111 : fViewMatrix(viewMatrix), fRRect(rrect), fLocalRect(localRect), fColor(color) {}
112 SkMatrix fViewMatrix;
113 SkRRect fRRect;
114 SkRect fLocalRect;
115 SkPMColor4f fColor;
116 Instance* fNext = nullptr;
117 };
118
119 Instance* fHeadInstance;
120 Instance** fTailInstance;
Robert Phillips366176b2020-02-26 11:40:50 -0500121 int fInstanceCount = 1;
Robert Phillips366176b2020-02-26 11:40:50 -0500122
123 sk_sp<const GrBuffer> fInstanceBuffer;
124 sk_sp<const GrBuffer> fVertexBuffer;
125 sk_sp<const GrBuffer> fIndexBuffer;
126 int fBaseInstance = 0;
Robert Phillips366176b2020-02-26 11:40:50 -0500127
128 // If this op is prePrepared the created programInfo will be stored here for use in
129 // onExecute. In the prePrepared case it will have been stored in the record-time arena.
130 GrProgramInfo* fProgramInfo = nullptr;
131
John Stiles7571f9e2020-09-02 22:42:33 -0400132 using INHERITED = GrMeshDrawOp;
Robert Phillips366176b2020-02-26 11:40:50 -0500133};
134
Robert Phillips360ec182020-03-26 13:29:50 -0400135GR_MAKE_BITFIELD_CLASS_OPS(FillRRectOp::ProcessorFlags)
Chris Dalton133944a2018-11-16 23:30:29 -0500136
137// Hardware derivatives are not always accurate enough for highly elliptical corners. This method
138// checks to make sure the corners will still all look good if we use HW derivatives.
Robert Phillips360ec182020-03-26 13:29:50 -0400139static bool can_use_hw_derivatives_with_coverage(const GrShaderCaps&,
140 const SkMatrix&,
141 const SkRRect&);
Chris Dalton133944a2018-11-16 23:30:29 -0500142
Herb Derbyc76d4092020-10-07 16:46:15 -0400143GrOp::Owner FillRRectOp::Make(GrRecordingContext* ctx,
Chris Dalton3c636f02021-07-08 14:50:57 -0600144 SkArenaAlloc* arena,
Herb Derbyc76d4092020-10-07 16:46:15 -0400145 GrPaint&& paint,
146 const SkMatrix& viewMatrix,
147 const SkRRect& rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600148 const SkRect& localRect,
Chris Dalton4f447342021-03-12 12:09:12 -0700149 GrAA aa) {
Robert Phillips360ec182020-03-26 13:29:50 -0400150 using Helper = GrSimpleMeshDrawOpHelper;
151
152 const GrCaps* caps = ctx->priv().caps();
153
Chris Daltona77cdee2020-04-03 14:50:43 -0600154 if (!caps->drawInstancedSupport()) {
Chris Dalton133944a2018-11-16 23:30:29 -0500155 return nullptr;
156 }
157
Chris Dalton61d694d2021-03-22 00:00:52 -0600158 // We transform into a normalized -1..+1 space to draw the round rect. If the boundaries are too
159 // large, the math can overflow. The caller can fall back on path rendering if this is the case.
160 if (std::max(rrect.height(), rrect.width()) >= 1e6f) {
161 return nullptr;
162 }
163
Robert Phillips360ec182020-03-26 13:29:50 -0400164 ProcessorFlags flags = ProcessorFlags::kNone;
Chris Daltoncc13b352021-03-05 14:59:01 -0700165 // TODO: Support perspective in a follow-on CL. This shouldn't be difficult, since we already
166 // use HW derivatives. The only trick will be adjusting the AA outset to account for
167 // perspective. (i.e., outset = 0.5 * z.)
168 if (viewMatrix.hasPerspective()) {
169 return nullptr;
170 }
171 if (can_use_hw_derivatives_with_coverage(*caps->shaderCaps(), viewMatrix, rrect)) {
172 // HW derivatives (more specifically, fwidth()) are consistently faster on all platforms in
173 // coverage mode. We use them as long as the approximation will be accurate enough.
174 flags |= ProcessorFlags::kUseHWDerivatives;
175 }
Chris Dalton4f447342021-03-12 12:09:12 -0700176 if (aa == GrAA::kNo) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700177 flags |= ProcessorFlags::kFakeNonAA;
Chris Dalton133944a2018-11-16 23:30:29 -0500178 }
179
Chris Dalton3c636f02021-07-08 14:50:57 -0600180 return Helper::FactoryHelper<FillRRectOp>(ctx, std::move(paint), arena, viewMatrix, rrect,
181 localRect, flags);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600182}
183
Herb Derbyc76d4092020-10-07 16:46:15 -0400184FillRRectOp::FillRRectOp(GrProcessorSet* processorSet,
Robert Phillips360ec182020-03-26 13:29:50 -0400185 const SkPMColor4f& paintColor,
Chris Dalton3c636f02021-07-08 14:50:57 -0600186 SkArenaAlloc* arena,
187 const SkMatrix& viewMatrix,
Robert Phillips360ec182020-03-26 13:29:50 -0400188 const SkRRect& rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600189 const SkRect& localRect,
Chris Dalton3c636f02021-07-08 14:50:57 -0600190 ProcessorFlags processorFlags)
Robert Phillipscad8fba2020-03-20 15:39:29 -0400191 : INHERITED(ClassID())
Chris Daltoncc13b352021-03-05 14:59:01 -0700192 , fHelper(processorSet,
193 (processorFlags & ProcessorFlags::kFakeNonAA)
194 ? GrAAType::kNone
195 : GrAAType::kCoverage) // Use analytic AA even if the RT is MSAA.
Robert Phillips360ec182020-03-26 13:29:50 -0400196 , fProcessorFlags(processorFlags & ~(ProcessorFlags::kHasLocalCoords |
Chris Daltoncc13b352021-03-05 14:59:01 -0700197 ProcessorFlags::kWideColor |
Chris Dalton3c636f02021-07-08 14:50:57 -0600198 ProcessorFlags::kMSAAEnabled))
199 , fHeadInstance(arena->make<Instance>(viewMatrix, rrect, localRect, paintColor))
200 , fTailInstance(&fHeadInstance->fNext) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700201 // FillRRectOp::Make fails if there is perspective.
Chris Dalton3c636f02021-07-08 14:50:57 -0600202 SkASSERT(!viewMatrix.hasPerspective());
203 this->setBounds(viewMatrix.mapRect(rrect.getBounds()),
204 GrOp::HasAABloat(!(processorFlags & ProcessorFlags::kFakeNonAA)),
Chris Dalton4f447342021-03-12 12:09:12 -0700205 GrOp::IsHairline::kNo);
Chris Dalton133944a2018-11-16 23:30:29 -0500206}
207
Chris Daltonea46ef32021-07-12 15:09:06 -0600208GrDrawOp::ClipResult FillRRectOp::clipToShape(GrSurfaceDrawContext* sdc, SkClipOp clipOp,
209 const SkMatrix& clipMatrix, const GrShape& shape,
210 GrAA aa) {
211 SkASSERT(fInstanceCount == 1); // This needs to be called before combining.
212 SkASSERT(fHeadInstance->fNext == nullptr);
213
214 if ((shape.isRect() || shape.isRRect()) &&
215 clipOp == SkClipOp::kIntersect &&
216 (aa == GrAA::kNo) == (fProcessorFlags & ProcessorFlags::kFakeNonAA)) {
217 // The clip shape is a round rect. Attempt to map it to a round rect in "viewMatrix" space.
218 SkRRect clipRRect;
219 if (clipMatrix == fHeadInstance->fViewMatrix) {
220 if (shape.isRect()) {
221 clipRRect.setRect(shape.rect());
222 } else {
223 clipRRect = shape.rrect();
224 }
225 } else {
226 // Find a matrix that maps from "clipMatrix" space to "viewMatrix" space.
227 SkASSERT(!fHeadInstance->fViewMatrix.hasPerspective());
228 if (clipMatrix.hasPerspective()) {
229 return ClipResult::kFail;
230 }
231 SkMatrix clipToView;
232 if (!fHeadInstance->fViewMatrix.invert(&clipToView)) {
233 return ClipResult::kClippedOut;
234 }
235 clipToView.preConcat(clipMatrix);
236 SkASSERT(!clipToView.hasPerspective());
237 if (!SkScalarNearlyZero(clipToView.getSkewX()) ||
238 !SkScalarNearlyZero(clipToView.getSkewY())) {
239 // A rect in "clipMatrix" space is not a rect in "viewMatrix" space.
240 return ClipResult::kFail;
241 }
242 clipToView.setSkewX(0);
243 clipToView.setSkewY(0);
244 SkASSERT(clipToView.rectStaysRect());
245
246 if (shape.isRect()) {
247 clipRRect.setRect(clipToView.mapRect(shape.rect()));
248 } else {
249 if (!shape.rrect().transform(clipToView, &clipRRect)) {
250 // Transforming the rrect failed. This shouldn't generally happen except in
251 // cases of fp32 overflow.
252 return ClipResult::kFail;
253 }
254 }
255 }
256
257 // Intersect our round rect with the clip shape.
258 SkRRect isectRRect;
259 if (fHeadInstance->fRRect.isRect() && clipRRect.isRect()) {
260 SkRect isectRect;
261 if (!isectRect.intersect(fHeadInstance->fRRect.rect(), clipRRect.rect())) {
262 return ClipResult::kClippedOut;
263 }
264 isectRRect.setRect(isectRect);
265 } else {
266 isectRRect = SkRRectPriv::ConservativeIntersect(fHeadInstance->fRRect, clipRRect);
267 if (isectRRect.isEmpty()) {
268 // The round rects did not intersect at all or the intersection was too complicated
269 // to compute quickly.
270 return ClipResult::kFail;
271 }
272 }
273
274 // Update the local rect.
275 auto rect = skvx::bit_pun<grvx::float4>(fHeadInstance->fRRect.rect());
276 auto local = skvx::bit_pun<grvx::float4>(fHeadInstance->fLocalRect);
277 auto isect = skvx::bit_pun<grvx::float4>(isectRRect.rect());
278 auto rectToLocalSize = (local - skvx::shuffle<2,3,0,1>(local)) /
279 (rect - skvx::shuffle<2,3,0,1>(rect));
280 fHeadInstance->fLocalRect = skvx::bit_pun<SkRect>((isect - rect) * rectToLocalSize + local);
281
282 // Update the round rect.
283 fHeadInstance->fRRect = isectRRect;
284 return ClipResult::kClippedGeometrically;
285 }
286
287 return ClipResult::kFail;
288}
289
Chris Dalton57ab06c2021-04-22 12:57:28 -0600290GrProcessorSet::Analysis FillRRectOp::finalize(const GrCaps& caps, const GrAppliedClip* clip,
291 GrClampType clampType) {
Chris Dalton3c636f02021-07-08 14:50:57 -0600292 SkASSERT(fInstanceCount == 1);
293 SkASSERT(fHeadInstance->fNext == nullptr);
Chris Dalton133944a2018-11-16 23:30:29 -0500294
Robert Phillips360ec182020-03-26 13:29:50 -0400295 bool isWideColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -0600296 auto analysis = fHelper.finalizeProcessors(caps, clip, clampType,
Chris Dalton3c636f02021-07-08 14:50:57 -0600297 GrProcessorAnalysisCoverage::kSingleChannel,
298 &fHeadInstance->fColor, &isWideColor);
Robert Phillips360ec182020-03-26 13:29:50 -0400299 if (isWideColor) {
300 fProcessorFlags |= ProcessorFlags::kWideColor;
Brian Osman5105d682019-02-13 16:06:14 -0500301 }
Chris Dalton133944a2018-11-16 23:30:29 -0500302 if (analysis.usesLocalCoords()) {
Robert Phillips360ec182020-03-26 13:29:50 -0400303 fProcessorFlags |= ProcessorFlags::kHasLocalCoords;
Chris Dalton133944a2018-11-16 23:30:29 -0500304 }
Chris Dalton4b62aed2019-01-15 11:53:00 -0700305 return analysis;
Chris Dalton133944a2018-11-16 23:30:29 -0500306}
307
Herb Derbye25c3002020-10-27 15:57:27 -0400308GrOp::CombineResult FillRRectOp::onCombineIfPossible(GrOp* op, SkArenaAlloc*, const GrCaps& caps) {
Robert Phillips366176b2020-02-26 11:40:50 -0500309 const auto& that = *op->cast<FillRRectOp>();
Chris Dalton3c636f02021-07-08 14:50:57 -0600310 if (!fHelper.isCompatible(that.fHelper, caps, this->bounds(), that.bounds()) ||
311 fProcessorFlags != that.fProcessorFlags) {
Robert Phillips360ec182020-03-26 13:29:50 -0400312 return CombineResult::kCannotCombine;
313 }
314
Chris Dalton3c636f02021-07-08 14:50:57 -0600315 *fTailInstance = that.fHeadInstance;
316 fTailInstance = that.fTailInstance;
Chris Dalton133944a2018-11-16 23:30:29 -0500317 fInstanceCount += that.fInstanceCount;
Chris Dalton133944a2018-11-16 23:30:29 -0500318 return CombineResult::kMerged;
319}
320
Robert Phillips366176b2020-02-26 11:40:50 -0500321class FillRRectOp::Processor : public GrGeometryProcessor {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600322public:
Robert Phillips360ec182020-03-26 13:29:50 -0400323 static GrGeometryProcessor* Make(SkArenaAlloc* arena, GrAAType aaType, ProcessorFlags flags) {
Mike Kleinf1241082020-12-14 15:59:09 -0600324 return arena->make([&](void* ptr) {
325 return new (ptr) Processor(aaType, flags);
326 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500327 }
328
Robert Phillips8053c972019-11-21 10:44:53 -0500329 const char* name() const final { return "GrFillRRectOp::Processor"; }
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500330
Robert Phillips8053c972019-11-21 10:44:53 -0500331 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const final {
Chris Daltoncc13b352021-03-05 14:59:01 -0700332 b->addBits(kNumProcessorFlags, (uint32_t)fFlags, "flags");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500333 }
334
Robert Phillipsf10535f2021-03-23 09:30:45 -0400335 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500336
337private:
Robert Phillips360ec182020-03-26 13:29:50 -0400338 Processor(GrAAType aaType, ProcessorFlags flags)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500339 : INHERITED(kGrFillRRectOp_Processor_ClassID)
Chris Dalton0dffbab2019-03-27 13:08:50 -0600340 , fFlags(flags) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700341 this->setVertexAttributes(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
Chris Dalton133944a2018-11-16 23:30:29 -0500342
Chris Daltoncc13b352021-03-05 14:59:01 -0700343 fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
344 fInstanceAttribs.emplace_back("translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600345 fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
346 fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
347 fColorAttrib = &fInstanceAttribs.push_back(
Robert Phillips360ec182020-03-26 13:29:50 -0400348 MakeColorAttribute("color", (fFlags & ProcessorFlags::kWideColor)));
349 if (fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600350 fInstanceAttribs.emplace_back(
351 "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
352 }
Chris Dalton3c636f02021-07-08 14:50:57 -0600353 SkASSERT(fInstanceAttribs.count() <= kMaxInstanceAttribs);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600354 this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
Chris Dalton0dffbab2019-03-27 13:08:50 -0600355 }
356
Chris Dalton0dffbab2019-03-27 13:08:50 -0600357 static constexpr Attribute kVertexAttribs[] = {
358 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
359 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
360 // Coverage only.
361 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
362
Robert Phillips360ec182020-03-26 13:29:50 -0400363 const ProcessorFlags fFlags;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600364
Chris Dalton3c636f02021-07-08 14:50:57 -0600365 constexpr static int kMaxInstanceAttribs = 6;
366 SkSTArray<kMaxInstanceAttribs, Attribute> fInstanceAttribs;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600367 const Attribute* fColorAttrib;
368
Chris Daltoncc13b352021-03-05 14:59:01 -0700369 class Impl;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500370
John Stiles7571f9e2020-09-02 22:42:33 -0400371 using INHERITED = GrGeometryProcessor;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600372};
373
Robert Phillips787fd9d2021-03-22 14:48:09 -0400374constexpr GrGeometryProcessor::Attribute FillRRectOp::Processor::kVertexAttribs[];
Chris Dalton0dffbab2019-03-27 13:08:50 -0600375
376// Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
Chris Dalton133944a2018-11-16 23:30:29 -0500377// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
378// edges. The Vertex struct tells the shader where to place its vertex within a normalized
379// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600380struct CoverageVertex {
Chris Dalton133944a2018-11-16 23:30:29 -0500381 std::array<float, 4> fRadiiSelector;
382 std::array<float, 2> fCorner;
383 std::array<float, 2> fRadiusOutset;
384 std::array<float, 2> fAABloatDirection;
385 float fCoverage;
386 float fIsLinearCoverage;
Chris Dalton133944a2018-11-16 23:30:29 -0500387};
388
389// This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
390// of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
391// rectangles.
392static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
393
Chris Daltoncc13b352021-03-05 14:59:01 -0700394static constexpr CoverageVertex kVertexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500395 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700396 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
397 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500398
399 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700400 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
401 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500402
403 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700404 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
405 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500406
407 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700408 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
409 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500410
411
412 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700413 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
414 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500415
416 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700417 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
418 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500419
420 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700421 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
422 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500423
424 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700425 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
426 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500427
428
429 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700430 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
431 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
432 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
433 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
434 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
435 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500436
437 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700438 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
439 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
440 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
441 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
442 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
443 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500444
445 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700446 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
447 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
448 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
449 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
450 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
451 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500452
453 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700454 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
455 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
456 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
457 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
Chris Dalton2d07e862018-11-26 12:30:47 -0700458 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700459 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500460
Chris Daltoncc13b352021-03-05 14:59:01 -0700461GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500462
Chris Daltoncc13b352021-03-05 14:59:01 -0700463static constexpr uint16_t kIndexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500464 // Inset octagon (solid coverage).
465 0, 1, 7,
466 1, 2, 7,
467 7, 2, 6,
468 2, 3, 6,
469 6, 3, 5,
470 3, 4, 5,
471
472 // AA borders (linear coverage).
473 0, 1, 8, 1, 9, 8,
474 2, 3, 10, 3, 11, 10,
475 4, 5, 12, 5, 13, 12,
476 6, 7, 14, 7, 15, 14,
477
478 // Top-left arc.
479 16, 17, 21,
480 17, 21, 18,
481 21, 18, 20,
482 18, 20, 19,
483
484 // Top-right arc.
485 22, 23, 27,
486 23, 27, 24,
487 27, 24, 26,
488 24, 26, 25,
489
490 // Bottom-right arc.
491 28, 29, 33,
492 29, 33, 30,
493 33, 30, 32,
494 30, 32, 31,
495
496 // Bottom-left arc.
497 34, 35, 39,
498 35, 39, 36,
499 39, 36, 38,
500 36, 38, 37};
501
Chris Daltoncc13b352021-03-05 14:59:01 -0700502GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400503
Robert Phillips71143952021-06-17 14:55:07 -0400504void FillRRectOp::onPrepareDraws(GrMeshDrawTarget* target) {
Chris Daltoncc29a392021-07-12 15:16:29 -0600505 if (target->usesMSAASurface()) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700506 fProcessorFlags |= ProcessorFlags::kMSAAEnabled;
507 }
508
Chris Dalton3c636f02021-07-08 14:50:57 -0600509 if (!fProgramInfo) {
510 this->createProgramInfo(target);
511 }
512
Chris Daltoncc29a392021-07-12 15:16:29 -0600513 // FIXME(skbug.com/12201): Our draw's MSAA state should match the render target, but DDL doesn't
514 // yet communicate DMSAA state to onPrePrepare.
515 SkASSERT(fProgramInfo->pipeline().isHWAntialiasState() == target->usesMSAASurface());
516
Chris Dalton3c636f02021-07-08 14:50:57 -0600517 size_t instanceStride = fProgramInfo->geomProc().instanceStride();
518
519 if (GrVertexWriter instanceWrter = target->makeVertexSpace(instanceStride, fInstanceCount,
520 &fInstanceBuffer, &fBaseInstance)) {
521 SkDEBUGCODE(auto end = instanceWrter.makeOffset(instanceStride * fInstanceCount));
522 for (Instance* i = fHeadInstance; i; i = i->fNext) {
523 auto [l, t, r, b] = i->fRRect.rect();
524
525 // Produce a matrix that draws the round rect from normalized [-1, -1, +1, +1] space.
526 SkMatrix m;
527 // Unmap the normalized rect [-1, -1, +1, +1] back to [l, t, r, b].
528 m.setScaleTranslate((r - l)/2, (b - t)/2, (l + r)/2, (t + b)/2);
529 // Map to device space.
530 m.postConcat(i->fViewMatrix);
531
532 // Convert the radii to [-1, -1, +1, +1] space and write their attribs.
533 grvx::float4 radiiX, radiiY;
534 grvx::strided_load2(&SkRRectPriv::GetRadiiArray(i->fRRect)->fX, radiiX, radiiY);
535 radiiX *= 2 / (r - l);
536 radiiY *= 2 / (b - t);
537
538 instanceWrter.write(
539 m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY(),
540 m.getTranslateX(), m.getTranslateY(),
541 radiiX,
542 radiiY,
543 GrVertexColor(i->fColor, fProcessorFlags & ProcessorFlags::kWideColor),
544 GrVertexWriter::If(fProcessorFlags & ProcessorFlags::kHasLocalCoords,
545 i->fLocalRect));
546 }
547 SkASSERT(instanceWrter == end);
Greg Danielf793de12019-09-05 13:23:23 -0400548 }
549
Chris Daltoncc13b352021-03-05 14:59:01 -0700550 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400551
Chris Daltoncc13b352021-03-05 14:59:01 -0700552 fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kIndex,
553 sizeof(kIndexData),
554 kIndexData, gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400555
Chris Daltoncc13b352021-03-05 14:59:01 -0700556 GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400557
Chris Daltoncc13b352021-03-05 14:59:01 -0700558 fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,
559 sizeof(kVertexData),
560 kVertexData,
561 gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400562}
563
Chris Daltoncc13b352021-03-05 14:59:01 -0700564class FillRRectOp::Processor::Impl : public GrGLSLGeometryProcessor {
Chris Dalton133944a2018-11-16 23:30:29 -0500565 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
John Stiles4d7ac492021-03-09 20:16:43 -0500566 GrGLSLVertexBuilder* v = args.fVertBuilder;
567 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
568
Robert Phillips787fd9d2021-03-22 14:48:09 -0400569 const auto& proc = args.fGeomProc.cast<Processor>();
Robert Phillips360ec182020-03-26 13:29:50 -0400570 bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
Chris Dalton133944a2018-11-16 23:30:29 -0500571
Chris Dalton0dffbab2019-03-27 13:08:50 -0600572 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
573
Chris Dalton133944a2018-11-16 23:30:29 -0500574 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
575 varyings->emitAttributes(proc);
John Stiles4d7ac492021-03-09 20:16:43 -0500576 f->codeAppendf("half4 %s;", args.fOutputColor);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600577 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500578 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
579
580 // Emit the vertex shader.
Chris Daltoncc13b352021-03-05 14:59:01 -0700581 // When MSAA is enabled, we need to make sure every sample gets lit up on pixels that have
582 // fractional coverage. We do this by making the ramp wider.
583 v->codeAppendf("float aa_bloat_multiplier = %i;",
584 (proc.fFlags & ProcessorFlags::kMSAAEnabled)
585 ? 2 // Outset an entire pixel (2 radii).
586 : (!(proc.fFlags & ProcessorFlags::kFakeNonAA))
587 ? 1 // Outset one half pixel (1 radius).
588 : 0); // No AA bloat.
589
Chris Dalton133944a2018-11-16 23:30:29 -0500590 // Unpack vertex attribs.
591 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
592 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
593 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
Chris Dalton133944a2018-11-16 23:30:29 -0500594 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
595
596 // Find the amount to bloat each edge for AA (in source space).
597 v->codeAppend("float2 pixellength = inversesqrt("
598 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
599 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
600 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
601 "abs(normalized_axis_dirs.zw));");
602 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
603
604 // Identify our radii.
Mike Reedd3efa992018-11-28 13:13:15 +0000605 v->codeAppend("float4 radii_and_neighbors = radii_selector"
606 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
607 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
608 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500609
Chris Daltoncc13b352021-03-05 14:59:01 -0700610 v->codeAppend("float coverage_multiplier = 1;");
Chris Dalton133944a2018-11-16 23:30:29 -0500611 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700612 // The rrect is more narrow than a half-pixel AA coverage ramp. We can't
613 // draw as-is or else opposite AA borders will overlap. Instead, fudge the
614 // size up to the width of a coverage ramp, and then reduce total coverage
615 // to make the rect appear more thin.
Chris Dalton133944a2018-11-16 23:30:29 -0500616 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
Chris Daltoncc13b352021-03-05 14:59:01 -0700617 v->codeAppend( "coverage_multiplier = 1 / (max(aa_bloatradius.x, 1) * "
618 "max(aa_bloatradius.y, 1));");
Chris Dalton133944a2018-11-16 23:30:29 -0500619 // Set radii to zero to ensure we take the "linear coverage" codepath.
620 // (The "coverage" variable only has effect in the linear codepath.)
621 v->codeAppend( "radii = float2(0);");
622 v->codeAppend("}");
623
Chris Daltoncc13b352021-03-05 14:59:01 -0700624 // Unpack coverage.
625 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
626 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
627 // MSAA has a wider ramp that goes from -.5 to 1.5 instead of 0 to 1.
628 v->codeAppendf("coverage = (coverage - .5) * aa_bloat_multiplier + .5;");
629 }
630
631 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.5))) {");
Chris Dalton133944a2018-11-16 23:30:29 -0500632 // The radii are very small. Demote this arc to a sharp 90 degree corner.
Chris Daltoncc13b352021-03-05 14:59:01 -0700633 v->codeAppend( "radii = float2(0);");
634 // Convert to a standard picture frame for an AA rect instead of the round
635 // rect geometry.
636 v->codeAppend( "aa_bloat_direction = sign(corner);");
637 v->codeAppend( "if (coverage > .5) {"); // Are we an inset edge?
638 v->codeAppend( "aa_bloat_direction = -aa_bloat_direction;");
639 v->codeAppend( "}");
Chris Dalton133944a2018-11-16 23:30:29 -0500640 v->codeAppend( "is_linear_coverage = 1;");
641 v->codeAppend("} else {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700642 // Don't let radii get smaller than a coverage ramp plus an extra half
643 // pixel for MSAA. Always use the same amount so we don't pop when
644 // switching between MSAA and coverage.
645 v->codeAppend( "radii = clamp(radii, pixellength * 1.5, 2 - pixellength * 1.5);");
646 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength * 1.5, "
647 "2 - pixellength * 1.5);");
Mike Reedd3efa992018-11-28 13:13:15 +0000648 // Don't let neighboring radii get closer together than 1/16 pixel.
649 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
650 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
651 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500652 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500653
654 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
655 // normalized [-1,-1,+1,+1] space.
Chris Daltoncc13b352021-03-05 14:59:01 -0700656 v->codeAppend("float2 aa_outset = "
657 "aa_bloat_direction * aa_bloatradius * aa_bloat_multiplier;");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700658 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500659
Chris Daltoncc13b352021-03-05 14:59:01 -0700660 v->codeAppend("if (coverage > .5) {"); // Are we an inset edge?
661 // Don't allow the aa insets to overlap. i.e., Don't let them inset past
662 // the center (x=y=0). Since we don't allow the rect to become thinner
663 // than 1px, this should only happen when using MSAA, where we inset by an
664 // entire pixel instead of half.
665 v->codeAppend( "if (aa_bloat_direction.x != 0 && vertexpos.x * corner.x < 0) {");
666 v->codeAppend( "float backset = abs(vertexpos.x);");
667 v->codeAppend( "vertexpos.x = 0;");
668 v->codeAppend( "vertexpos.y += "
669 "backset * sign(corner.y) * pixellength.y/pixellength.x;");
670 v->codeAppend( "coverage = (coverage - .5) * abs(corner.x) / "
671 "(abs(corner.x) + backset) + .5;");
672 v->codeAppend( "}");
673 v->codeAppend( "if (aa_bloat_direction.y != 0 && vertexpos.y * corner.y < 0) {");
674 v->codeAppend( "float backset = abs(vertexpos.y);");
675 v->codeAppend( "vertexpos.y = 0;");
676 v->codeAppend( "vertexpos.x += "
677 "backset * sign(corner.x) * pixellength.x/pixellength.y;");
678 v->codeAppend( "coverage = (coverage - .5) * abs(corner.y) / "
679 "(abs(corner.y) + backset) + .5;");
680 v->codeAppend( "}");
681 v->codeAppend("}");
682
Michael Ludwig553db622020-06-19 10:47:30 -0400683 // Write positions
Chris Dalton133944a2018-11-16 23:30:29 -0500684 GrShaderVar localCoord("", kFloat2_GrSLType);
Robert Phillips360ec182020-03-26 13:29:50 -0400685 if (proc.fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton133944a2018-11-16 23:30:29 -0500686 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
687 "local_rect.zw * (1 + vertexpos)) * .5;");
Michael Ludwig553db622020-06-19 10:47:30 -0400688 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
Chris Dalton133944a2018-11-16 23:30:29 -0500689 }
Chris Dalton133944a2018-11-16 23:30:29 -0500690
691 // Transform to device space.
692 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
693 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
694 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
695
696 // Setup interpolants for coverage.
697 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
698 varyings->addVarying("arccoord", &arcCoord);
699 v->codeAppend("if (0 != is_linear_coverage) {");
700 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
701 // interpolate linear coverage across y.
Chris Daltoncc13b352021-03-05 14:59:01 -0700702 v->codeAppendf( "%s.xy = float2(0, coverage * coverage_multiplier);",
703 arcCoord.vsOut());
Chris Dalton133944a2018-11-16 23:30:29 -0500704 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700705 // Find the normalized arc coordinates for our corner ellipse.
706 // (i.e., the coordinate system where x^2 + y^2 == 1).
707 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500708 // We are a corner piece: Interpolate the arc coordinates for coverage.
709 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
710 // instructs the fragment shader to use linear coverage).
711 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
712 if (!useHWDerivatives) {
713 // The gradient is order-1: Interpolate it across arccoord.zw.
714 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
715 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
716 }
717 v->codeAppend("}");
718
719 // Emit the fragment shader.
Chris Dalton133944a2018-11-16 23:30:29 -0500720 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
721 f->codeAppendf("half coverage;");
722 f->codeAppendf("if (0 == x_plus_1) {");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600723 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500724 f->codeAppendf("} else {");
725 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
726 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
727 if (useHWDerivatives) {
728 f->codeAppendf("float fnwidth = fwidth(fn);");
729 } else {
730 // The gradient is interpolated across arccoord.zw.
731 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
732 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
733 }
Chris Daltoncc13b352021-03-05 14:59:01 -0700734 f->codeAppendf( "coverage = .5 - half(fn/fnwidth);");
735 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
736 // MSAA uses ramps larger than 1px, so we need to clamp in both branches.
737 f->codeAppendf("}");
738 }
739 f->codeAppendf("coverage = clamp(coverage, 0, 1);");
740 if (!(proc.fFlags & ProcessorFlags::kMSAAEnabled)) {
741 // When not using MSAA, we only need to clamp in the "arc" branch.
742 f->codeAppendf("}");
743 }
744 if (proc.fFlags & ProcessorFlags::kFakeNonAA) {
745 f->codeAppendf("coverage = (coverage >= .5) ? 1 : 0;");
746 }
John Stiles4d7ac492021-03-09 20:16:43 -0500747 f->codeAppendf("half4 %s = half4(coverage);", args.fOutputCoverage);
Chris Dalton133944a2018-11-16 23:30:29 -0500748 }
749
Brian Salomon5a328282021-04-14 10:32:25 -0400750 void setData(const GrGLSLProgramDataManager&,
751 const GrShaderCaps&,
752 const GrGeometryProcessor&) override {}
Chris Dalton133944a2018-11-16 23:30:29 -0500753};
754
Chris Dalton0dffbab2019-03-27 13:08:50 -0600755
Robert Phillipsf10535f2021-03-23 09:30:45 -0400756GrGLSLGeometryProcessor* FillRRectOp::Processor::createGLSLInstance(const GrShaderCaps&) const {
Chris Daltoncc13b352021-03-05 14:59:01 -0700757 return new Impl();
Chris Dalton133944a2018-11-16 23:30:29 -0500758}
759
Robert Phillipscad8fba2020-03-20 15:39:29 -0400760void FillRRectOp::onCreateProgramInfo(const GrCaps* caps,
761 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -0500762 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400763 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -0400764 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500765 GrXferBarrierFlags renderPassXferBarriers,
766 GrLoadOp colorLoadOp) {
Chris Daltoncc29a392021-07-12 15:16:29 -0600767 if (writeView.asRenderTargetProxy()->numSamples() > 1) {
768 fProcessorFlags |= ProcessorFlags::kMSAAEnabled;
769 }
770 auto extraPipelineFlags = (fProcessorFlags & ProcessorFlags::kMSAAEnabled)
771 ? GrPipeline::InputFlags::kHWAntialias
772 : GrPipeline::InputFlags::kNone;
Robert Phillips360ec182020-03-26 13:29:50 -0400773 GrGeometryProcessor* gp = Processor::Make(arena, fHelper.aaType(), fProcessorFlags);
Chris Daltoncc29a392021-07-12 15:16:29 -0600774 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(
775 caps, arena, writeView, std::move(appliedClip), dstProxyView, gp,
776 fHelper.detachProcessorSet(), GrPrimitiveType::kTriangles, renderPassXferBarriers,
777 colorLoadOp, fHelper.pipelineFlags() | extraPipelineFlags);
Robert Phillips8053c972019-11-21 10:44:53 -0500778}
779
Robert Phillips366176b2020-02-26 11:40:50 -0500780void FillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Robert Phillips8053c972019-11-21 10:44:53 -0500781 if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
782 return; // Setup failed.
783 }
784
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600785 flushState->bindPipelineAndScissorClip(*fProgramInfo, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400786 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Greg Daniel426274b2020-07-20 11:37:38 -0400787 flushState->bindBuffers(std::move(fIndexBuffer), std::move(fInstanceBuffer),
788 std::move(fVertexBuffer));
Chris Daltoncc13b352021-03-05 14:59:01 -0700789 flushState->drawIndexedInstanced(SK_ARRAY_COUNT(kIndexData), 0, fInstanceCount, fBaseInstance,
790 0);
Chris Dalton133944a2018-11-16 23:30:29 -0500791}
792
793// Will the given corner look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600794static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
Chris Dalton133944a2018-11-16 23:30:29 -0500795 Sk2f devRadii = devScale * cornerRadii;
796 if (devRadii[1] < devRadii[0]) {
797 devRadii = SkNx_shuffle<1,0>(devRadii);
798 }
Brian Osman788b9162020-02-07 10:36:46 -0500799 float minDevRadius = std::max(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
Chris Dalton133944a2018-11-16 23:30:29 -0500800 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
801 // This threshold was arrived at subjevtively on an NVIDIA chip.
802 return minDevRadius * minDevRadius * 5 > devRadii[1];
803}
804
Chris Dalton0dffbab2019-03-27 13:08:50 -0600805static bool can_use_hw_derivatives_with_coverage(
806 const Sk2f& devScale, const SkVector& cornerRadii) {
807 return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
Chris Dalton133944a2018-11-16 23:30:29 -0500808}
809
810// Will the given round rect look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600811static bool can_use_hw_derivatives_with_coverage(
812 const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
Chris Dalton133944a2018-11-16 23:30:29 -0500813 if (!shaderCaps.shaderDerivativeSupport()) {
814 return false;
815 }
816
817 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
818 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
819 Sk2f devScale = (x*x + y*y).sqrt();
820 switch (rrect.getType()) {
821 case SkRRect::kEmpty_Type:
822 case SkRRect::kRect_Type:
823 return true;
824
825 case SkRRect::kOval_Type:
826 case SkRRect::kSimple_Type:
Chris Dalton0dffbab2019-03-27 13:08:50 -0600827 return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
Chris Dalton133944a2018-11-16 23:30:29 -0500828
829 case SkRRect::kNinePatch_Type: {
830 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
831 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
832 Sk2f minRadii = Sk2f::Min(r0, r1);
833 Sk2f maxRadii = Sk2f::Max(r0, r1);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600834 return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
835 can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
Chris Dalton133944a2018-11-16 23:30:29 -0500836 }
837
838 case SkRRect::kComplex_Type: {
839 for (int i = 0; i < 4; ++i) {
840 auto corner = static_cast<SkRRect::Corner>(i);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600841 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
Chris Dalton133944a2018-11-16 23:30:29 -0500842 return false;
843 }
844 }
845 return true;
846 }
847 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600848 SK_ABORT("Invalid round rect type.");
Chris Dalton133944a2018-11-16 23:30:29 -0500849}
Robert Phillips366176b2020-02-26 11:40:50 -0500850
851} // anonymous namespace
852
853
Herb Derbyc76d4092020-10-07 16:46:15 -0400854GrOp::Owner GrFillRRectOp::Make(GrRecordingContext* ctx,
Chris Dalton3c636f02021-07-08 14:50:57 -0600855 SkArenaAlloc* arena,
Herb Derbyc76d4092020-10-07 16:46:15 -0400856 GrPaint&& paint,
857 const SkMatrix& viewMatrix,
858 const SkRRect& rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600859 const SkRect& localRect,
Chris Dalton4f447342021-03-12 12:09:12 -0700860 GrAA aa) {
Chris Dalton3c636f02021-07-08 14:50:57 -0600861 return FillRRectOp::Make(ctx, arena, std::move(paint), viewMatrix, rrect, localRect, aa);
Robert Phillips366176b2020-02-26 11:40:50 -0500862}
863
Chris Dalton61d694d2021-03-22 00:00:52 -0600864
Robert Phillips366176b2020-02-26 11:40:50 -0500865#if GR_TEST_UTILS
866
867#include "src/gpu/GrDrawOpTest.h"
868
869GR_DRAW_OP_TEST_DEFINE(FillRRectOp) {
Chris Dalton3c636f02021-07-08 14:50:57 -0600870 SkArenaAlloc arena(64 * sizeof(float));
Robert Phillips366176b2020-02-26 11:40:50 -0500871 SkMatrix viewMatrix = GrTest::TestMatrix(random);
Chris Dalton4f447342021-03-12 12:09:12 -0700872 GrAA aa = GrAA(random->nextBool());
Robert Phillips366176b2020-02-26 11:40:50 -0500873
874 SkRect rect = GrTest::TestRect(random);
875 float w = rect.width();
876 float h = rect.height();
877
878 SkRRect rrect;
879 // TODO: test out other rrect configurations
880 rrect.setNinePatch(rect, w / 3.0f, h / 4.0f, w / 5.0f, h / 6.0);
881
882 return GrFillRRectOp::Make(context,
Chris Dalton3c636f02021-07-08 14:50:57 -0600883 &arena,
Robert Phillips360ec182020-03-26 13:29:50 -0400884 std::move(paint),
Robert Phillips366176b2020-02-26 11:40:50 -0500885 viewMatrix,
886 rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600887 rrect.rect(),
Chris Dalton4f447342021-03-12 12:09:12 -0700888 aa);
Robert Phillips366176b2020-02-26 11:40:50 -0500889}
890
891#endif