blob: 6251a792b1a77a35e99b45cc256b43ee0f52d26b [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"
18#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
19#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
20#include "src/gpu/glsl/GrGLSLVarying.h"
21#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
Robert Phillipscad8fba2020-03-20 15:39:29 -040022#include "src/gpu/ops/GrMeshDrawOp.h"
Robert Phillipsce978572020-02-28 11:56:44 -050023#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
Robert Phillips366176b2020-02-26 11:40:50 -050024
25namespace {
26
Robert Phillipscad8fba2020-03-20 15:39:29 -040027class FillRRectOp : public GrMeshDrawOp {
Robert Phillips360ec182020-03-26 13:29:50 -040028private:
29 using Helper = GrSimpleMeshDrawOpHelper;
30
Robert Phillips366176b2020-02-26 11:40:50 -050031public:
32 DEFINE_OP_CLASS_ID
33
Herb Derbyc76d4092020-10-07 16:46:15 -040034 static GrOp::Owner Make(GrRecordingContext*,
35 GrPaint&&,
36 const SkMatrix& viewMatrix,
37 const SkRRect&,
Chris Dalton61d694d2021-03-22 00:00:52 -060038 const SkRect& localRect,
Chris Dalton4f447342021-03-12 12:09:12 -070039 GrAA);
Robert Phillips366176b2020-02-26 11:40:50 -050040
41 const char* name() const final { return "GrFillRRectOp"; }
42
Robert Phillips360ec182020-03-26 13:29:50 -040043 FixedFunctionFlags fixedFunctionFlags() const final { return fHelper.fixedFunctionFlags(); }
44
Chris Dalton57ab06c2021-04-22 12:57:28 -060045 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) final;
Herb Derbye25c3002020-10-27 15:57:27 -040046 CombineResult onCombineIfPossible(GrOp*, SkArenaAlloc*, const GrCaps&) final;
Robert Phillips360ec182020-03-26 13:29:50 -040047
Robert Phillips294723d2021-06-17 09:23:58 -040048 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips366176b2020-02-26 11:40:50 -050049 if (fProgramInfo) {
Robert Phillips294723d2021-06-17 09:23:58 -040050 fProgramInfo->visitFPProxies(func);
Robert Phillips366176b2020-02-26 11:40:50 -050051 } else {
Robert Phillips294723d2021-06-17 09:23:58 -040052 fHelper.visitProxies(func);
Robert Phillips366176b2020-02-26 11:40:50 -050053 }
54 }
55
Robert Phillips71143952021-06-17 14:55:07 -040056 void onPrepareDraws(GrMeshDrawTarget*) final;
Robert Phillips366176b2020-02-26 11:40:50 -050057
58 void onExecute(GrOpFlushState*, const SkRect& chainBounds) final;
59
60private:
Robert Phillips360ec182020-03-26 13:29:50 -040061 friend class ::GrSimpleMeshDrawOpHelper; // for access to ctor
Herb Derbyc76d4092020-10-07 16:46:15 -040062 friend class ::GrOp; // for access to ctor
Robert Phillips360ec182020-03-26 13:29:50 -040063
64 enum class ProcessorFlags {
Robert Phillips366176b2020-02-26 11:40:50 -050065 kNone = 0,
66 kUseHWDerivatives = 1 << 0,
Chris Daltoncc13b352021-03-05 14:59:01 -070067 kHasLocalCoords = 1 << 1,
68 kWideColor = 1 << 2,
69 kMSAAEnabled = 1 << 3,
70 kFakeNonAA = 1 << 4,
Robert Phillips366176b2020-02-26 11:40:50 -050071 };
Chris Daltoncc13b352021-03-05 14:59:01 -070072 constexpr static int kNumProcessorFlags = 5;
Robert Phillips366176b2020-02-26 11:40:50 -050073
Robert Phillips360ec182020-03-26 13:29:50 -040074 GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(ProcessorFlags);
Robert Phillips366176b2020-02-26 11:40:50 -050075
76 class Processor;
77
Herb Derbyc76d4092020-10-07 16:46:15 -040078 FillRRectOp(GrProcessorSet*,
Robert Phillips360ec182020-03-26 13:29:50 -040079 const SkPMColor4f& paintColor,
80 const SkMatrix& totalShapeMatrix,
81 const SkRRect&,
Chris Dalton61d694d2021-03-22 00:00:52 -060082 const SkRect& localRect,
Robert Phillips360ec182020-03-26 13:29:50 -040083 ProcessorFlags,
84 const SkRect& devBounds);
Robert Phillips366176b2020-02-26 11:40:50 -050085
86 // These methods are used to append data of various POD types to our internal array of instance
87 // data. The actual layout of the instance buffer can vary from Op to Op.
88 template <typename T> inline T* appendInstanceData(int count) {
89 static_assert(std::is_pod<T>::value, "");
90 static_assert(4 == alignof(T), "");
91 return reinterpret_cast<T*>(fInstanceData.push_back_n(sizeof(T) * count));
92 }
93
94 template <typename T, typename... Args>
95 inline void writeInstanceData(const T& val, const Args&... remainder) {
96 memcpy(this->appendInstanceData<T>(1), &val, sizeof(T));
97 this->writeInstanceData(remainder...);
98 }
99
100 void writeInstanceData() {} // Halt condition.
101
Robert Phillipscad8fba2020-03-20 15:39:29 -0400102 GrProgramInfo* programInfo() final { return fProgramInfo; }
103
Robert Phillips366176b2020-02-26 11:40:50 -0500104 // Create a GrProgramInfo object in the provided arena
Robert Phillipscad8fba2020-03-20 15:39:29 -0400105 void onCreateProgramInfo(const GrCaps*,
106 SkArenaAlloc*,
Adlai Hollere2296f72020-11-19 13:41:26 -0500107 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400108 GrAppliedClip&&,
John Stiles52cb1d02021-06-02 11:58:05 -0400109 const GrDstProxyView&,
Greg Daniel42dbca52020-11-20 10:22:43 -0500110 GrXferBarrierFlags renderPassXferBarriers,
111 GrLoadOp colorLoadOp) final;
Robert Phillips366176b2020-02-26 11:40:50 -0500112
Robert Phillips360ec182020-03-26 13:29:50 -0400113 Helper fHelper;
114 SkPMColor4f fColor;
115 const SkRect fLocalRect;
116 ProcessorFlags fProcessorFlags;
Robert Phillips366176b2020-02-26 11:40:50 -0500117
118 SkSTArray<sizeof(float) * 16 * 4, char, /*MEM_MOVE=*/ true> fInstanceData;
119 int fInstanceCount = 1;
120 int fInstanceStride = 0;
121
122 sk_sp<const GrBuffer> fInstanceBuffer;
123 sk_sp<const GrBuffer> fVertexBuffer;
124 sk_sp<const GrBuffer> fIndexBuffer;
125 int fBaseInstance = 0;
Robert Phillips366176b2020-02-26 11:40:50 -0500126
127 // If this op is prePrepared the created programInfo will be stored here for use in
128 // onExecute. In the prePrepared case it will have been stored in the record-time arena.
129 GrProgramInfo* fProgramInfo = nullptr;
130
John Stiles7571f9e2020-09-02 22:42:33 -0400131 using INHERITED = GrMeshDrawOp;
Robert Phillips366176b2020-02-26 11:40:50 -0500132};
133
Robert Phillips360ec182020-03-26 13:29:50 -0400134GR_MAKE_BITFIELD_CLASS_OPS(FillRRectOp::ProcessorFlags)
Chris Dalton133944a2018-11-16 23:30:29 -0500135
136// Hardware derivatives are not always accurate enough for highly elliptical corners. This method
137// checks to make sure the corners will still all look good if we use HW derivatives.
Robert Phillips360ec182020-03-26 13:29:50 -0400138static bool can_use_hw_derivatives_with_coverage(const GrShaderCaps&,
139 const SkMatrix&,
140 const SkRRect&);
Chris Dalton133944a2018-11-16 23:30:29 -0500141
Herb Derbyc76d4092020-10-07 16:46:15 -0400142GrOp::Owner FillRRectOp::Make(GrRecordingContext* ctx,
143 GrPaint&& paint,
144 const SkMatrix& viewMatrix,
145 const SkRRect& rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600146 const SkRect& localRect,
Chris Dalton4f447342021-03-12 12:09:12 -0700147 GrAA aa) {
Robert Phillips360ec182020-03-26 13:29:50 -0400148 using Helper = GrSimpleMeshDrawOpHelper;
149
150 const GrCaps* caps = ctx->priv().caps();
151
Chris Daltona77cdee2020-04-03 14:50:43 -0600152 if (!caps->drawInstancedSupport()) {
Chris Dalton133944a2018-11-16 23:30:29 -0500153 return nullptr;
154 }
155
Chris Dalton61d694d2021-03-22 00:00:52 -0600156 // We transform into a normalized -1..+1 space to draw the round rect. If the boundaries are too
157 // large, the math can overflow. The caller can fall back on path rendering if this is the case.
158 if (std::max(rrect.height(), rrect.width()) >= 1e6f) {
159 return nullptr;
160 }
161
Robert Phillips360ec182020-03-26 13:29:50 -0400162 ProcessorFlags flags = ProcessorFlags::kNone;
Chris Daltoncc13b352021-03-05 14:59:01 -0700163 // TODO: Support perspective in a follow-on CL. This shouldn't be difficult, since we already
164 // use HW derivatives. The only trick will be adjusting the AA outset to account for
165 // perspective. (i.e., outset = 0.5 * z.)
166 if (viewMatrix.hasPerspective()) {
167 return nullptr;
168 }
169 if (can_use_hw_derivatives_with_coverage(*caps->shaderCaps(), viewMatrix, rrect)) {
170 // HW derivatives (more specifically, fwidth()) are consistently faster on all platforms in
171 // coverage mode. We use them as long as the approximation will be accurate enough.
172 flags |= ProcessorFlags::kUseHWDerivatives;
173 }
Chris Dalton4f447342021-03-12 12:09:12 -0700174 if (aa == GrAA::kNo) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700175 flags |= ProcessorFlags::kFakeNonAA;
Chris Dalton133944a2018-11-16 23:30:29 -0500176 }
177
178 // Produce a matrix that draws the round rect from normalized [-1, -1, +1, +1] space.
179 float l = rrect.rect().left(), r = rrect.rect().right(),
180 t = rrect.rect().top(), b = rrect.rect().bottom();
181 SkMatrix m;
182 // Unmap the normalized rect [-1, -1, +1, +1] back to [l, t, r, b].
183 m.setScaleTranslate((r - l)/2, (b - t)/2, (l + r)/2, (t + b)/2);
184 // Map to device space.
185 m.postConcat(viewMatrix);
186
Chris Dalton0dffbab2019-03-27 13:08:50 -0600187 SkRect devBounds;
Chris Daltoncc13b352021-03-05 14:59:01 -0700188 // Since m is an affine matrix that maps the rect [-1, -1, +1, +1] into the shape's
189 // device-space quad, it's quite simple to find the bounding rectangle:
190 devBounds = SkRect::MakeXYWH(m.getTranslateX(), m.getTranslateY(), 0, 0);
191 devBounds.outset(SkScalarAbs(m.getScaleX()) + SkScalarAbs(m.getSkewX()),
192 SkScalarAbs(m.getSkewY()) + SkScalarAbs(m.getScaleY()));
Chris Dalton0dffbab2019-03-27 13:08:50 -0600193
Chris Dalton61d694d2021-03-22 00:00:52 -0600194 return Helper::FactoryHelper<FillRRectOp>(ctx, std::move(paint), m, rrect, localRect, flags,
195 devBounds);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600196}
197
Herb Derbyc76d4092020-10-07 16:46:15 -0400198FillRRectOp::FillRRectOp(GrProcessorSet* processorSet,
Robert Phillips360ec182020-03-26 13:29:50 -0400199 const SkPMColor4f& paintColor,
200 const SkMatrix& totalShapeMatrix,
201 const SkRRect& rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600202 const SkRect& localRect,
Robert Phillips360ec182020-03-26 13:29:50 -0400203 ProcessorFlags processorFlags,
Robert Phillips366176b2020-02-26 11:40:50 -0500204 const SkRect& devBounds)
Robert Phillipscad8fba2020-03-20 15:39:29 -0400205 : INHERITED(ClassID())
Chris Daltoncc13b352021-03-05 14:59:01 -0700206 , fHelper(processorSet,
207 (processorFlags & ProcessorFlags::kFakeNonAA)
208 ? GrAAType::kNone
209 : GrAAType::kCoverage) // Use analytic AA even if the RT is MSAA.
Robert Phillips360ec182020-03-26 13:29:50 -0400210 , fColor(paintColor)
Chris Dalton61d694d2021-03-22 00:00:52 -0600211 , fLocalRect(localRect)
Robert Phillips360ec182020-03-26 13:29:50 -0400212 , fProcessorFlags(processorFlags & ~(ProcessorFlags::kHasLocalCoords |
Chris Daltoncc13b352021-03-05 14:59:01 -0700213 ProcessorFlags::kWideColor |
214 ProcessorFlags::kMSAAEnabled)) {
215 // FillRRectOp::Make fails if there is perspective.
216 SkASSERT(!totalShapeMatrix.hasPerspective());
Chris Dalton4f447342021-03-12 12:09:12 -0700217 this->setBounds(devBounds, GrOp::HasAABloat(!(processorFlags & ProcessorFlags::kFakeNonAA)),
218 GrOp::IsHairline::kNo);
Chris Dalton133944a2018-11-16 23:30:29 -0500219
220 // Write the matrix attribs.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600221 const SkMatrix& m = totalShapeMatrix;
Chris Daltoncc13b352021-03-05 14:59:01 -0700222 // Affine 2D transformation (float2x2 plus float2 translate).
223 SkASSERT(!m.hasPerspective());
224 this->writeInstanceData(m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY());
225 this->writeInstanceData(m.getTranslateX(), m.getTranslateY());
Chris Dalton133944a2018-11-16 23:30:29 -0500226
227 // Convert the radii to [-1, -1, +1, +1] space and write their attribs.
228 Sk4f radiiX, radiiY;
229 Sk4f::Load2(SkRRectPriv::GetRadiiArray(rrect), &radiiX, &radiiY);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600230 (radiiX * (2/rrect.width())).store(this->appendInstanceData<float>(4));
231 (radiiY * (2/rrect.height())).store(this->appendInstanceData<float>(4));
Chris Dalton133944a2018-11-16 23:30:29 -0500232
233 // We will write the color and local rect attribs during finalize().
234}
235
Chris Dalton57ab06c2021-04-22 12:57:28 -0600236GrProcessorSet::Analysis FillRRectOp::finalize(const GrCaps& caps, const GrAppliedClip* clip,
237 GrClampType clampType) {
Chris Dalton133944a2018-11-16 23:30:29 -0500238 SkASSERT(1 == fInstanceCount);
239
Robert Phillips360ec182020-03-26 13:29:50 -0400240 bool isWideColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -0600241 auto analysis = fHelper.finalizeProcessors(caps, clip, clampType,
242 GrProcessorAnalysisCoverage::kSingleChannel, &fColor,
243 &isWideColor);
Chris Dalton133944a2018-11-16 23:30:29 -0500244
245 // Finish writing the instance attribs.
Robert Phillips360ec182020-03-26 13:29:50 -0400246 if (isWideColor) {
247 fProcessorFlags |= ProcessorFlags::kWideColor;
248 this->writeInstanceData(fColor);
Brian Osman5105d682019-02-13 16:06:14 -0500249 } else {
Robert Phillips360ec182020-03-26 13:29:50 -0400250 this->writeInstanceData(fColor.toBytes_RGBA());
Brian Osman5105d682019-02-13 16:06:14 -0500251 }
252
Chris Dalton133944a2018-11-16 23:30:29 -0500253 if (analysis.usesLocalCoords()) {
Robert Phillips360ec182020-03-26 13:29:50 -0400254 fProcessorFlags |= ProcessorFlags::kHasLocalCoords;
Chris Dalton133944a2018-11-16 23:30:29 -0500255 this->writeInstanceData(fLocalRect);
Chris Dalton133944a2018-11-16 23:30:29 -0500256 }
257 fInstanceStride = fInstanceData.count();
258
Chris Dalton4b62aed2019-01-15 11:53:00 -0700259 return analysis;
Chris Dalton133944a2018-11-16 23:30:29 -0500260}
261
Herb Derbye25c3002020-10-27 15:57:27 -0400262GrOp::CombineResult FillRRectOp::onCombineIfPossible(GrOp* op, SkArenaAlloc*, const GrCaps& caps) {
Robert Phillips366176b2020-02-26 11:40:50 -0500263 const auto& that = *op->cast<FillRRectOp>();
Robert Phillips360ec182020-03-26 13:29:50 -0400264 if (!fHelper.isCompatible(that.fHelper, caps, this->bounds(), that.bounds())) {
265 return CombineResult::kCannotCombine;
266 }
267
268 if (fProcessorFlags != that.fProcessorFlags ||
Chris Dalton133944a2018-11-16 23:30:29 -0500269 fInstanceData.count() > std::numeric_limits<int>::max() - that.fInstanceData.count()) {
270 return CombineResult::kCannotCombine;
271 }
272
273 fInstanceData.push_back_n(that.fInstanceData.count(), that.fInstanceData.begin());
274 fInstanceCount += that.fInstanceCount;
275 SkASSERT(fInstanceStride == that.fInstanceStride);
276 return CombineResult::kMerged;
277}
278
Robert Phillips366176b2020-02-26 11:40:50 -0500279class FillRRectOp::Processor : public GrGeometryProcessor {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600280public:
Robert Phillips360ec182020-03-26 13:29:50 -0400281 static GrGeometryProcessor* Make(SkArenaAlloc* arena, GrAAType aaType, ProcessorFlags flags) {
Mike Kleinf1241082020-12-14 15:59:09 -0600282 return arena->make([&](void* ptr) {
283 return new (ptr) Processor(aaType, flags);
284 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500285 }
286
Robert Phillips8053c972019-11-21 10:44:53 -0500287 const char* name() const final { return "GrFillRRectOp::Processor"; }
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500288
Robert Phillips8053c972019-11-21 10:44:53 -0500289 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const final {
Chris Daltoncc13b352021-03-05 14:59:01 -0700290 b->addBits(kNumProcessorFlags, (uint32_t)fFlags, "flags");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500291 }
292
Robert Phillipsf10535f2021-03-23 09:30:45 -0400293 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500294
295private:
Robert Phillips360ec182020-03-26 13:29:50 -0400296 Processor(GrAAType aaType, ProcessorFlags flags)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500297 : INHERITED(kGrFillRRectOp_Processor_ClassID)
Chris Dalton0dffbab2019-03-27 13:08:50 -0600298 , fFlags(flags) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700299 this->setVertexAttributes(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
Chris Dalton133944a2018-11-16 23:30:29 -0500300
Chris Daltoncc13b352021-03-05 14:59:01 -0700301 fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
302 fInstanceAttribs.emplace_back("translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600303 fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
304 fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
305 fColorAttrib = &fInstanceAttribs.push_back(
Robert Phillips360ec182020-03-26 13:29:50 -0400306 MakeColorAttribute("color", (fFlags & ProcessorFlags::kWideColor)));
307 if (fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600308 fInstanceAttribs.emplace_back(
309 "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
310 }
311 this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
Chris Dalton0dffbab2019-03-27 13:08:50 -0600312 }
313
Chris Dalton0dffbab2019-03-27 13:08:50 -0600314 static constexpr Attribute kVertexAttribs[] = {
315 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
316 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
317 // Coverage only.
318 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
319
Robert Phillips360ec182020-03-26 13:29:50 -0400320 const ProcessorFlags fFlags;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600321
322 SkSTArray<6, Attribute> fInstanceAttribs;
323 const Attribute* fColorAttrib;
324
Chris Daltoncc13b352021-03-05 14:59:01 -0700325 class Impl;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500326
John Stiles7571f9e2020-09-02 22:42:33 -0400327 using INHERITED = GrGeometryProcessor;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600328};
329
Robert Phillips787fd9d2021-03-22 14:48:09 -0400330constexpr GrGeometryProcessor::Attribute FillRRectOp::Processor::kVertexAttribs[];
Chris Dalton0dffbab2019-03-27 13:08:50 -0600331
332// Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
Chris Dalton133944a2018-11-16 23:30:29 -0500333// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
334// edges. The Vertex struct tells the shader where to place its vertex within a normalized
335// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600336struct CoverageVertex {
Chris Dalton133944a2018-11-16 23:30:29 -0500337 std::array<float, 4> fRadiiSelector;
338 std::array<float, 2> fCorner;
339 std::array<float, 2> fRadiusOutset;
340 std::array<float, 2> fAABloatDirection;
341 float fCoverage;
342 float fIsLinearCoverage;
Chris Dalton133944a2018-11-16 23:30:29 -0500343};
344
345// This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
346// of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
347// rectangles.
348static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
349
Chris Daltoncc13b352021-03-05 14:59:01 -0700350static constexpr CoverageVertex kVertexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500351 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700352 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
353 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500354
355 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700356 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
357 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500358
359 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700360 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
361 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500362
363 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700364 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
365 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500366
367
368 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700369 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
370 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500371
372 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700373 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
374 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500375
376 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700377 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
378 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500379
380 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700381 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
382 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500383
384
385 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700386 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
387 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
388 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
389 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
390 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
391 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500392
393 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700394 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
395 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
396 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
397 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
398 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
399 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500400
401 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700402 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
403 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
404 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
405 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
406 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
407 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500408
409 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700410 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
411 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
412 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
413 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
Chris Dalton2d07e862018-11-26 12:30:47 -0700414 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700415 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500416
Chris Daltoncc13b352021-03-05 14:59:01 -0700417GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500418
Chris Daltoncc13b352021-03-05 14:59:01 -0700419static constexpr uint16_t kIndexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500420 // Inset octagon (solid coverage).
421 0, 1, 7,
422 1, 2, 7,
423 7, 2, 6,
424 2, 3, 6,
425 6, 3, 5,
426 3, 4, 5,
427
428 // AA borders (linear coverage).
429 0, 1, 8, 1, 9, 8,
430 2, 3, 10, 3, 11, 10,
431 4, 5, 12, 5, 13, 12,
432 6, 7, 14, 7, 15, 14,
433
434 // Top-left arc.
435 16, 17, 21,
436 17, 21, 18,
437 21, 18, 20,
438 18, 20, 19,
439
440 // Top-right arc.
441 22, 23, 27,
442 23, 27, 24,
443 27, 24, 26,
444 24, 26, 25,
445
446 // Bottom-right arc.
447 28, 29, 33,
448 29, 33, 30,
449 33, 30, 32,
450 30, 32, 31,
451
452 // Bottom-left arc.
453 34, 35, 39,
454 35, 39, 36,
455 39, 36, 38,
456 36, 38, 37};
457
Chris Daltoncc13b352021-03-05 14:59:01 -0700458GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400459
Robert Phillips71143952021-06-17 14:55:07 -0400460void FillRRectOp::onPrepareDraws(GrMeshDrawTarget* target) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700461 // We request no multisample, but some platforms don't support disabling it on MSAA targets.
Chris Dalton2517ce32021-04-13 00:21:15 -0600462 if (target->usesMSAASurface() && !target->caps().multisampleDisableSupport()) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700463 fProcessorFlags |= ProcessorFlags::kMSAAEnabled;
464 }
465
Robert Phillipscad8fba2020-03-20 15:39:29 -0400466 if (void* instanceData = target->makeVertexSpace(fInstanceStride, fInstanceCount,
467 &fInstanceBuffer, &fBaseInstance)) {
Greg Danielf793de12019-09-05 13:23:23 -0400468 SkASSERT(fInstanceStride * fInstanceCount == fInstanceData.count());
469 memcpy(instanceData, fInstanceData.begin(), fInstanceData.count());
470 }
471
Chris Daltoncc13b352021-03-05 14:59:01 -0700472 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400473
Chris Daltoncc13b352021-03-05 14:59:01 -0700474 fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kIndex,
475 sizeof(kIndexData),
476 kIndexData, gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400477
Chris Daltoncc13b352021-03-05 14:59:01 -0700478 GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400479
Chris Daltoncc13b352021-03-05 14:59:01 -0700480 fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,
481 sizeof(kVertexData),
482 kVertexData,
483 gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400484}
485
Chris Daltoncc13b352021-03-05 14:59:01 -0700486class FillRRectOp::Processor::Impl : public GrGLSLGeometryProcessor {
Chris Dalton133944a2018-11-16 23:30:29 -0500487 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
John Stiles4d7ac492021-03-09 20:16:43 -0500488 GrGLSLVertexBuilder* v = args.fVertBuilder;
489 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
490
Robert Phillips787fd9d2021-03-22 14:48:09 -0400491 const auto& proc = args.fGeomProc.cast<Processor>();
Robert Phillips360ec182020-03-26 13:29:50 -0400492 bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
Chris Dalton133944a2018-11-16 23:30:29 -0500493
Chris Dalton0dffbab2019-03-27 13:08:50 -0600494 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
495
Chris Dalton133944a2018-11-16 23:30:29 -0500496 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
497 varyings->emitAttributes(proc);
John Stiles4d7ac492021-03-09 20:16:43 -0500498 f->codeAppendf("half4 %s;", args.fOutputColor);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600499 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500500 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
501
502 // Emit the vertex shader.
Chris Daltoncc13b352021-03-05 14:59:01 -0700503 // When MSAA is enabled, we need to make sure every sample gets lit up on pixels that have
504 // fractional coverage. We do this by making the ramp wider.
505 v->codeAppendf("float aa_bloat_multiplier = %i;",
506 (proc.fFlags & ProcessorFlags::kMSAAEnabled)
507 ? 2 // Outset an entire pixel (2 radii).
508 : (!(proc.fFlags & ProcessorFlags::kFakeNonAA))
509 ? 1 // Outset one half pixel (1 radius).
510 : 0); // No AA bloat.
511
Chris Dalton133944a2018-11-16 23:30:29 -0500512 // Unpack vertex attribs.
513 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
514 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
515 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
Chris Dalton133944a2018-11-16 23:30:29 -0500516 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
517
518 // Find the amount to bloat each edge for AA (in source space).
519 v->codeAppend("float2 pixellength = inversesqrt("
520 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
521 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
522 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
523 "abs(normalized_axis_dirs.zw));");
524 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
525
526 // Identify our radii.
Mike Reedd3efa992018-11-28 13:13:15 +0000527 v->codeAppend("float4 radii_and_neighbors = radii_selector"
528 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
529 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
530 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500531
Chris Daltoncc13b352021-03-05 14:59:01 -0700532 v->codeAppend("float coverage_multiplier = 1;");
Chris Dalton133944a2018-11-16 23:30:29 -0500533 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700534 // The rrect is more narrow than a half-pixel AA coverage ramp. We can't
535 // draw as-is or else opposite AA borders will overlap. Instead, fudge the
536 // size up to the width of a coverage ramp, and then reduce total coverage
537 // to make the rect appear more thin.
Chris Dalton133944a2018-11-16 23:30:29 -0500538 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
Chris Daltoncc13b352021-03-05 14:59:01 -0700539 v->codeAppend( "coverage_multiplier = 1 / (max(aa_bloatradius.x, 1) * "
540 "max(aa_bloatradius.y, 1));");
Chris Dalton133944a2018-11-16 23:30:29 -0500541 // Set radii to zero to ensure we take the "linear coverage" codepath.
542 // (The "coverage" variable only has effect in the linear codepath.)
543 v->codeAppend( "radii = float2(0);");
544 v->codeAppend("}");
545
Chris Daltoncc13b352021-03-05 14:59:01 -0700546 // Unpack coverage.
547 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
548 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
549 // MSAA has a wider ramp that goes from -.5 to 1.5 instead of 0 to 1.
550 v->codeAppendf("coverage = (coverage - .5) * aa_bloat_multiplier + .5;");
551 }
552
553 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.5))) {");
Chris Dalton133944a2018-11-16 23:30:29 -0500554 // The radii are very small. Demote this arc to a sharp 90 degree corner.
Chris Daltoncc13b352021-03-05 14:59:01 -0700555 v->codeAppend( "radii = float2(0);");
556 // Convert to a standard picture frame for an AA rect instead of the round
557 // rect geometry.
558 v->codeAppend( "aa_bloat_direction = sign(corner);");
559 v->codeAppend( "if (coverage > .5) {"); // Are we an inset edge?
560 v->codeAppend( "aa_bloat_direction = -aa_bloat_direction;");
561 v->codeAppend( "}");
Chris Dalton133944a2018-11-16 23:30:29 -0500562 v->codeAppend( "is_linear_coverage = 1;");
563 v->codeAppend("} else {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700564 // Don't let radii get smaller than a coverage ramp plus an extra half
565 // pixel for MSAA. Always use the same amount so we don't pop when
566 // switching between MSAA and coverage.
567 v->codeAppend( "radii = clamp(radii, pixellength * 1.5, 2 - pixellength * 1.5);");
568 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength * 1.5, "
569 "2 - pixellength * 1.5);");
Mike Reedd3efa992018-11-28 13:13:15 +0000570 // Don't let neighboring radii get closer together than 1/16 pixel.
571 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
572 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
573 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500574 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500575
576 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
577 // normalized [-1,-1,+1,+1] space.
Chris Daltoncc13b352021-03-05 14:59:01 -0700578 v->codeAppend("float2 aa_outset = "
579 "aa_bloat_direction * aa_bloatradius * aa_bloat_multiplier;");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700580 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500581
Chris Daltoncc13b352021-03-05 14:59:01 -0700582 v->codeAppend("if (coverage > .5) {"); // Are we an inset edge?
583 // Don't allow the aa insets to overlap. i.e., Don't let them inset past
584 // the center (x=y=0). Since we don't allow the rect to become thinner
585 // than 1px, this should only happen when using MSAA, where we inset by an
586 // entire pixel instead of half.
587 v->codeAppend( "if (aa_bloat_direction.x != 0 && vertexpos.x * corner.x < 0) {");
588 v->codeAppend( "float backset = abs(vertexpos.x);");
589 v->codeAppend( "vertexpos.x = 0;");
590 v->codeAppend( "vertexpos.y += "
591 "backset * sign(corner.y) * pixellength.y/pixellength.x;");
592 v->codeAppend( "coverage = (coverage - .5) * abs(corner.x) / "
593 "(abs(corner.x) + backset) + .5;");
594 v->codeAppend( "}");
595 v->codeAppend( "if (aa_bloat_direction.y != 0 && vertexpos.y * corner.y < 0) {");
596 v->codeAppend( "float backset = abs(vertexpos.y);");
597 v->codeAppend( "vertexpos.y = 0;");
598 v->codeAppend( "vertexpos.x += "
599 "backset * sign(corner.x) * pixellength.x/pixellength.y;");
600 v->codeAppend( "coverage = (coverage - .5) * abs(corner.y) / "
601 "(abs(corner.y) + backset) + .5;");
602 v->codeAppend( "}");
603 v->codeAppend("}");
604
Michael Ludwig553db622020-06-19 10:47:30 -0400605 // Write positions
Chris Dalton133944a2018-11-16 23:30:29 -0500606 GrShaderVar localCoord("", kFloat2_GrSLType);
Robert Phillips360ec182020-03-26 13:29:50 -0400607 if (proc.fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton133944a2018-11-16 23:30:29 -0500608 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
609 "local_rect.zw * (1 + vertexpos)) * .5;");
Michael Ludwig553db622020-06-19 10:47:30 -0400610 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
Chris Dalton133944a2018-11-16 23:30:29 -0500611 }
Chris Dalton133944a2018-11-16 23:30:29 -0500612
613 // Transform to device space.
614 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
615 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
616 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
617
618 // Setup interpolants for coverage.
619 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
620 varyings->addVarying("arccoord", &arcCoord);
621 v->codeAppend("if (0 != is_linear_coverage) {");
622 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
623 // interpolate linear coverage across y.
Chris Daltoncc13b352021-03-05 14:59:01 -0700624 v->codeAppendf( "%s.xy = float2(0, coverage * coverage_multiplier);",
625 arcCoord.vsOut());
Chris Dalton133944a2018-11-16 23:30:29 -0500626 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700627 // Find the normalized arc coordinates for our corner ellipse.
628 // (i.e., the coordinate system where x^2 + y^2 == 1).
629 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500630 // We are a corner piece: Interpolate the arc coordinates for coverage.
631 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
632 // instructs the fragment shader to use linear coverage).
633 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
634 if (!useHWDerivatives) {
635 // The gradient is order-1: Interpolate it across arccoord.zw.
636 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
637 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
638 }
639 v->codeAppend("}");
640
641 // Emit the fragment shader.
Chris Dalton133944a2018-11-16 23:30:29 -0500642 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
643 f->codeAppendf("half coverage;");
644 f->codeAppendf("if (0 == x_plus_1) {");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600645 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500646 f->codeAppendf("} else {");
647 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
648 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
649 if (useHWDerivatives) {
650 f->codeAppendf("float fnwidth = fwidth(fn);");
651 } else {
652 // The gradient is interpolated across arccoord.zw.
653 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
654 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
655 }
Chris Daltoncc13b352021-03-05 14:59:01 -0700656 f->codeAppendf( "coverage = .5 - half(fn/fnwidth);");
657 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
658 // MSAA uses ramps larger than 1px, so we need to clamp in both branches.
659 f->codeAppendf("}");
660 }
661 f->codeAppendf("coverage = clamp(coverage, 0, 1);");
662 if (!(proc.fFlags & ProcessorFlags::kMSAAEnabled)) {
663 // When not using MSAA, we only need to clamp in the "arc" branch.
664 f->codeAppendf("}");
665 }
666 if (proc.fFlags & ProcessorFlags::kFakeNonAA) {
667 f->codeAppendf("coverage = (coverage >= .5) ? 1 : 0;");
668 }
John Stiles4d7ac492021-03-09 20:16:43 -0500669 f->codeAppendf("half4 %s = half4(coverage);", args.fOutputCoverage);
Chris Dalton133944a2018-11-16 23:30:29 -0500670 }
671
Brian Salomon5a328282021-04-14 10:32:25 -0400672 void setData(const GrGLSLProgramDataManager&,
673 const GrShaderCaps&,
674 const GrGeometryProcessor&) override {}
Chris Dalton133944a2018-11-16 23:30:29 -0500675};
676
Chris Dalton0dffbab2019-03-27 13:08:50 -0600677
Robert Phillipsf10535f2021-03-23 09:30:45 -0400678GrGLSLGeometryProcessor* FillRRectOp::Processor::createGLSLInstance(const GrShaderCaps&) const {
Chris Daltoncc13b352021-03-05 14:59:01 -0700679 return new Impl();
Chris Dalton133944a2018-11-16 23:30:29 -0500680}
681
Robert Phillipscad8fba2020-03-20 15:39:29 -0400682void FillRRectOp::onCreateProgramInfo(const GrCaps* caps,
683 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -0500684 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400685 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -0400686 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500687 GrXferBarrierFlags renderPassXferBarriers,
688 GrLoadOp colorLoadOp) {
Robert Phillips360ec182020-03-26 13:29:50 -0400689 GrGeometryProcessor* gp = Processor::Make(arena, fHelper.aaType(), fProcessorFlags);
Robert Phillipsce978572020-02-28 11:56:44 -0500690 SkASSERT(gp->instanceStride() == (size_t)fInstanceStride);
Chris Dalton133944a2018-11-16 23:30:29 -0500691
Brian Salomon8afde5f2020-04-01 16:22:00 -0400692 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -0400693 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -0500694 renderPassXferBarriers, colorLoadOp);
Robert Phillips8053c972019-11-21 10:44:53 -0500695}
696
Robert Phillips366176b2020-02-26 11:40:50 -0500697void FillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Robert Phillips8053c972019-11-21 10:44:53 -0500698 if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
699 return; // Setup failed.
700 }
701
702 if (!fProgramInfo) {
Robert Phillipscad8fba2020-03-20 15:39:29 -0400703 this->createProgramInfo(flushState);
Robert Phillips8053c972019-11-21 10:44:53 -0500704 }
Robert Phillips901aff02019-10-08 12:32:56 -0400705
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600706 flushState->bindPipelineAndScissorClip(*fProgramInfo, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400707 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Greg Daniel426274b2020-07-20 11:37:38 -0400708 flushState->bindBuffers(std::move(fIndexBuffer), std::move(fInstanceBuffer),
709 std::move(fVertexBuffer));
Chris Daltoncc13b352021-03-05 14:59:01 -0700710 flushState->drawIndexedInstanced(SK_ARRAY_COUNT(kIndexData), 0, fInstanceCount, fBaseInstance,
711 0);
Chris Dalton133944a2018-11-16 23:30:29 -0500712}
713
714// Will the given corner look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600715static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
Chris Dalton133944a2018-11-16 23:30:29 -0500716 Sk2f devRadii = devScale * cornerRadii;
717 if (devRadii[1] < devRadii[0]) {
718 devRadii = SkNx_shuffle<1,0>(devRadii);
719 }
Brian Osman788b9162020-02-07 10:36:46 -0500720 float minDevRadius = std::max(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
Chris Dalton133944a2018-11-16 23:30:29 -0500721 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
722 // This threshold was arrived at subjevtively on an NVIDIA chip.
723 return minDevRadius * minDevRadius * 5 > devRadii[1];
724}
725
Chris Dalton0dffbab2019-03-27 13:08:50 -0600726static bool can_use_hw_derivatives_with_coverage(
727 const Sk2f& devScale, const SkVector& cornerRadii) {
728 return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
Chris Dalton133944a2018-11-16 23:30:29 -0500729}
730
731// Will the given round rect look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600732static bool can_use_hw_derivatives_with_coverage(
733 const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
Chris Dalton133944a2018-11-16 23:30:29 -0500734 if (!shaderCaps.shaderDerivativeSupport()) {
735 return false;
736 }
737
738 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
739 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
740 Sk2f devScale = (x*x + y*y).sqrt();
741 switch (rrect.getType()) {
742 case SkRRect::kEmpty_Type:
743 case SkRRect::kRect_Type:
744 return true;
745
746 case SkRRect::kOval_Type:
747 case SkRRect::kSimple_Type:
Chris Dalton0dffbab2019-03-27 13:08:50 -0600748 return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
Chris Dalton133944a2018-11-16 23:30:29 -0500749
750 case SkRRect::kNinePatch_Type: {
751 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
752 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
753 Sk2f minRadii = Sk2f::Min(r0, r1);
754 Sk2f maxRadii = Sk2f::Max(r0, r1);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600755 return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
756 can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
Chris Dalton133944a2018-11-16 23:30:29 -0500757 }
758
759 case SkRRect::kComplex_Type: {
760 for (int i = 0; i < 4; ++i) {
761 auto corner = static_cast<SkRRect::Corner>(i);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600762 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
Chris Dalton133944a2018-11-16 23:30:29 -0500763 return false;
764 }
765 }
766 return true;
767 }
768 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600769 SK_ABORT("Invalid round rect type.");
Chris Dalton133944a2018-11-16 23:30:29 -0500770}
Robert Phillips366176b2020-02-26 11:40:50 -0500771
772} // anonymous namespace
773
774
Herb Derbyc76d4092020-10-07 16:46:15 -0400775GrOp::Owner GrFillRRectOp::Make(GrRecordingContext* ctx,
776 GrPaint&& paint,
777 const SkMatrix& viewMatrix,
778 const SkRRect& rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600779 const SkRect& localRect,
Chris Dalton4f447342021-03-12 12:09:12 -0700780 GrAA aa) {
Chris Dalton61d694d2021-03-22 00:00:52 -0600781 return FillRRectOp::Make(ctx, std::move(paint), viewMatrix, rrect, localRect, aa);
Robert Phillips366176b2020-02-26 11:40:50 -0500782}
783
Chris Dalton61d694d2021-03-22 00:00:52 -0600784
Robert Phillips366176b2020-02-26 11:40:50 -0500785#if GR_TEST_UTILS
786
787#include "src/gpu/GrDrawOpTest.h"
788
789GR_DRAW_OP_TEST_DEFINE(FillRRectOp) {
Robert Phillips366176b2020-02-26 11:40:50 -0500790 SkMatrix viewMatrix = GrTest::TestMatrix(random);
Chris Dalton4f447342021-03-12 12:09:12 -0700791 GrAA aa = GrAA(random->nextBool());
Robert Phillips366176b2020-02-26 11:40:50 -0500792
793 SkRect rect = GrTest::TestRect(random);
794 float w = rect.width();
795 float h = rect.height();
796
797 SkRRect rrect;
798 // TODO: test out other rrect configurations
799 rrect.setNinePatch(rect, w / 3.0f, h / 4.0f, w / 5.0f, h / 6.0);
800
801 return GrFillRRectOp::Make(context,
Robert Phillips360ec182020-03-26 13:29:50 -0400802 std::move(paint),
Robert Phillips366176b2020-02-26 11:40:50 -0500803 viewMatrix,
804 rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600805 rrect.rect(),
Chris Dalton4f447342021-03-12 12:09:12 -0700806 aa);
Robert Phillips366176b2020-02-26 11:40:50 -0500807}
808
809#endif