blob: 00bb14fe2320cf8d7815d2350508e97ee71ee603 [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
Robert Phillips366176b2020-02-26 11:40:50 -050045 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
46 bool hasMixedSampledCoverage, GrClampType) final;
Herb Derbye25c3002020-10-27 15:57:27 -040047 CombineResult onCombineIfPossible(GrOp*, SkArenaAlloc*, const GrCaps&) final;
Robert Phillips360ec182020-03-26 13:29:50 -040048
Robert Phillips366176b2020-02-26 11:40:50 -050049 void visitProxies(const VisitProxyFunc& fn) const override {
50 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -060051 fProgramInfo->visitFPProxies(fn);
Robert Phillips366176b2020-02-26 11:40:50 -050052 } else {
Robert Phillips360ec182020-03-26 13:29:50 -040053 fHelper.visitProxies(fn);
Robert Phillips366176b2020-02-26 11:40:50 -050054 }
55 }
56
Robert Phillipscad8fba2020-03-20 15:39:29 -040057 void onPrepareDraws(Target*) final;
Robert Phillips366176b2020-02-26 11:40:50 -050058
59 void onExecute(GrOpFlushState*, const SkRect& chainBounds) final;
60
61private:
Robert Phillips360ec182020-03-26 13:29:50 -040062 friend class ::GrSimpleMeshDrawOpHelper; // for access to ctor
Herb Derbyc76d4092020-10-07 16:46:15 -040063 friend class ::GrOp; // for access to ctor
Robert Phillips360ec182020-03-26 13:29:50 -040064
65 enum class ProcessorFlags {
Robert Phillips366176b2020-02-26 11:40:50 -050066 kNone = 0,
67 kUseHWDerivatives = 1 << 0,
Chris Daltoncc13b352021-03-05 14:59:01 -070068 kHasLocalCoords = 1 << 1,
69 kWideColor = 1 << 2,
70 kMSAAEnabled = 1 << 3,
71 kFakeNonAA = 1 << 4,
Robert Phillips366176b2020-02-26 11:40:50 -050072 };
Chris Daltoncc13b352021-03-05 14:59:01 -070073 constexpr static int kNumProcessorFlags = 5;
Robert Phillips366176b2020-02-26 11:40:50 -050074
Robert Phillips360ec182020-03-26 13:29:50 -040075 GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(ProcessorFlags);
Robert Phillips366176b2020-02-26 11:40:50 -050076
77 class Processor;
78
Herb Derbyc76d4092020-10-07 16:46:15 -040079 FillRRectOp(GrProcessorSet*,
Robert Phillips360ec182020-03-26 13:29:50 -040080 const SkPMColor4f& paintColor,
81 const SkMatrix& totalShapeMatrix,
82 const SkRRect&,
Chris Dalton61d694d2021-03-22 00:00:52 -060083 const SkRect& localRect,
Robert Phillips360ec182020-03-26 13:29:50 -040084 ProcessorFlags,
85 const SkRect& devBounds);
Robert Phillips366176b2020-02-26 11:40:50 -050086
87 // These methods are used to append data of various POD types to our internal array of instance
88 // data. The actual layout of the instance buffer can vary from Op to Op.
89 template <typename T> inline T* appendInstanceData(int count) {
90 static_assert(std::is_pod<T>::value, "");
91 static_assert(4 == alignof(T), "");
92 return reinterpret_cast<T*>(fInstanceData.push_back_n(sizeof(T) * count));
93 }
94
95 template <typename T, typename... Args>
96 inline void writeInstanceData(const T& val, const Args&... remainder) {
97 memcpy(this->appendInstanceData<T>(1), &val, sizeof(T));
98 this->writeInstanceData(remainder...);
99 }
100
101 void writeInstanceData() {} // Halt condition.
102
Robert Phillipscad8fba2020-03-20 15:39:29 -0400103 GrProgramInfo* programInfo() final { return fProgramInfo; }
104
Robert Phillips366176b2020-02-26 11:40:50 -0500105 // Create a GrProgramInfo object in the provided arena
Robert Phillipscad8fba2020-03-20 15:39:29 -0400106 void onCreateProgramInfo(const GrCaps*,
107 SkArenaAlloc*,
Adlai Hollere2296f72020-11-19 13:41:26 -0500108 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400109 GrAppliedClip&&,
Greg Danield358cbe2020-09-11 09:33:54 -0400110 const GrXferProcessor::DstProxyView&,
Greg Daniel42dbca52020-11-20 10:22:43 -0500111 GrXferBarrierFlags renderPassXferBarriers,
112 GrLoadOp colorLoadOp) final;
Robert Phillips366176b2020-02-26 11:40:50 -0500113
Robert Phillips360ec182020-03-26 13:29:50 -0400114 Helper fHelper;
115 SkPMColor4f fColor;
116 const SkRect fLocalRect;
117 ProcessorFlags fProcessorFlags;
Robert Phillips366176b2020-02-26 11:40:50 -0500118
119 SkSTArray<sizeof(float) * 16 * 4, char, /*MEM_MOVE=*/ true> fInstanceData;
120 int fInstanceCount = 1;
121 int fInstanceStride = 0;
122
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,
144 GrPaint&& paint,
145 const SkMatrix& viewMatrix,
146 const SkRRect& rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600147 const SkRect& localRect,
Chris Dalton4f447342021-03-12 12:09:12 -0700148 GrAA aa) {
Robert Phillips360ec182020-03-26 13:29:50 -0400149 using Helper = GrSimpleMeshDrawOpHelper;
150
151 const GrCaps* caps = ctx->priv().caps();
152
Chris Daltona77cdee2020-04-03 14:50:43 -0600153 if (!caps->drawInstancedSupport()) {
Chris Dalton133944a2018-11-16 23:30:29 -0500154 return nullptr;
155 }
156
Chris Dalton61d694d2021-03-22 00:00:52 -0600157 // We transform into a normalized -1..+1 space to draw the round rect. If the boundaries are too
158 // large, the math can overflow. The caller can fall back on path rendering if this is the case.
159 if (std::max(rrect.height(), rrect.width()) >= 1e6f) {
160 return nullptr;
161 }
162
Robert Phillips360ec182020-03-26 13:29:50 -0400163 ProcessorFlags flags = ProcessorFlags::kNone;
Chris Daltoncc13b352021-03-05 14:59:01 -0700164 // TODO: Support perspective in a follow-on CL. This shouldn't be difficult, since we already
165 // use HW derivatives. The only trick will be adjusting the AA outset to account for
166 // perspective. (i.e., outset = 0.5 * z.)
167 if (viewMatrix.hasPerspective()) {
168 return nullptr;
169 }
170 if (can_use_hw_derivatives_with_coverage(*caps->shaderCaps(), viewMatrix, rrect)) {
171 // HW derivatives (more specifically, fwidth()) are consistently faster on all platforms in
172 // coverage mode. We use them as long as the approximation will be accurate enough.
173 flags |= ProcessorFlags::kUseHWDerivatives;
174 }
Chris Dalton4f447342021-03-12 12:09:12 -0700175 if (aa == GrAA::kNo) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700176 flags |= ProcessorFlags::kFakeNonAA;
Chris Dalton133944a2018-11-16 23:30:29 -0500177 }
178
179 // Produce a matrix that draws the round rect from normalized [-1, -1, +1, +1] space.
180 float l = rrect.rect().left(), r = rrect.rect().right(),
181 t = rrect.rect().top(), b = rrect.rect().bottom();
182 SkMatrix m;
183 // Unmap the normalized rect [-1, -1, +1, +1] back to [l, t, r, b].
184 m.setScaleTranslate((r - l)/2, (b - t)/2, (l + r)/2, (t + b)/2);
185 // Map to device space.
186 m.postConcat(viewMatrix);
187
Chris Dalton0dffbab2019-03-27 13:08:50 -0600188 SkRect devBounds;
Chris Daltoncc13b352021-03-05 14:59:01 -0700189 // Since m is an affine matrix that maps the rect [-1, -1, +1, +1] into the shape's
190 // device-space quad, it's quite simple to find the bounding rectangle:
191 devBounds = SkRect::MakeXYWH(m.getTranslateX(), m.getTranslateY(), 0, 0);
192 devBounds.outset(SkScalarAbs(m.getScaleX()) + SkScalarAbs(m.getSkewX()),
193 SkScalarAbs(m.getSkewY()) + SkScalarAbs(m.getScaleY()));
Chris Dalton0dffbab2019-03-27 13:08:50 -0600194
Chris Dalton61d694d2021-03-22 00:00:52 -0600195 return Helper::FactoryHelper<FillRRectOp>(ctx, std::move(paint), m, rrect, localRect, flags,
196 devBounds);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600197}
198
Herb Derbyc76d4092020-10-07 16:46:15 -0400199FillRRectOp::FillRRectOp(GrProcessorSet* processorSet,
Robert Phillips360ec182020-03-26 13:29:50 -0400200 const SkPMColor4f& paintColor,
201 const SkMatrix& totalShapeMatrix,
202 const SkRRect& rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600203 const SkRect& localRect,
Robert Phillips360ec182020-03-26 13:29:50 -0400204 ProcessorFlags processorFlags,
Robert Phillips366176b2020-02-26 11:40:50 -0500205 const SkRect& devBounds)
Robert Phillipscad8fba2020-03-20 15:39:29 -0400206 : INHERITED(ClassID())
Chris Daltoncc13b352021-03-05 14:59:01 -0700207 , fHelper(processorSet,
208 (processorFlags & ProcessorFlags::kFakeNonAA)
209 ? GrAAType::kNone
210 : GrAAType::kCoverage) // Use analytic AA even if the RT is MSAA.
Robert Phillips360ec182020-03-26 13:29:50 -0400211 , fColor(paintColor)
Chris Dalton61d694d2021-03-22 00:00:52 -0600212 , fLocalRect(localRect)
Robert Phillips360ec182020-03-26 13:29:50 -0400213 , fProcessorFlags(processorFlags & ~(ProcessorFlags::kHasLocalCoords |
Chris Daltoncc13b352021-03-05 14:59:01 -0700214 ProcessorFlags::kWideColor |
215 ProcessorFlags::kMSAAEnabled)) {
216 // FillRRectOp::Make fails if there is perspective.
217 SkASSERT(!totalShapeMatrix.hasPerspective());
Chris Dalton4f447342021-03-12 12:09:12 -0700218 this->setBounds(devBounds, GrOp::HasAABloat(!(processorFlags & ProcessorFlags::kFakeNonAA)),
219 GrOp::IsHairline::kNo);
Chris Dalton133944a2018-11-16 23:30:29 -0500220
221 // Write the matrix attribs.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600222 const SkMatrix& m = totalShapeMatrix;
Chris Daltoncc13b352021-03-05 14:59:01 -0700223 // Affine 2D transformation (float2x2 plus float2 translate).
224 SkASSERT(!m.hasPerspective());
225 this->writeInstanceData(m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY());
226 this->writeInstanceData(m.getTranslateX(), m.getTranslateY());
Chris Dalton133944a2018-11-16 23:30:29 -0500227
228 // Convert the radii to [-1, -1, +1, +1] space and write their attribs.
229 Sk4f radiiX, radiiY;
230 Sk4f::Load2(SkRRectPriv::GetRadiiArray(rrect), &radiiX, &radiiY);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600231 (radiiX * (2/rrect.width())).store(this->appendInstanceData<float>(4));
232 (radiiY * (2/rrect.height())).store(this->appendInstanceData<float>(4));
Chris Dalton133944a2018-11-16 23:30:29 -0500233
234 // We will write the color and local rect attribs during finalize().
235}
236
Robert Phillips366176b2020-02-26 11:40:50 -0500237GrProcessorSet::Analysis FillRRectOp::finalize(
Chris Dalton6ce447a2019-06-23 18:07:38 -0600238 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
239 GrClampType clampType) {
Chris Dalton133944a2018-11-16 23:30:29 -0500240 SkASSERT(1 == fInstanceCount);
241
Robert Phillips360ec182020-03-26 13:29:50 -0400242 bool isWideColor;
243 auto analysis = fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
244 GrProcessorAnalysisCoverage::kSingleChannel,
245 &fColor, &isWideColor);
Chris Dalton133944a2018-11-16 23:30:29 -0500246
247 // Finish writing the instance attribs.
Robert Phillips360ec182020-03-26 13:29:50 -0400248 if (isWideColor) {
249 fProcessorFlags |= ProcessorFlags::kWideColor;
250 this->writeInstanceData(fColor);
Brian Osman5105d682019-02-13 16:06:14 -0500251 } else {
Robert Phillips360ec182020-03-26 13:29:50 -0400252 this->writeInstanceData(fColor.toBytes_RGBA());
Brian Osman5105d682019-02-13 16:06:14 -0500253 }
254
Chris Dalton133944a2018-11-16 23:30:29 -0500255 if (analysis.usesLocalCoords()) {
Robert Phillips360ec182020-03-26 13:29:50 -0400256 fProcessorFlags |= ProcessorFlags::kHasLocalCoords;
Chris Dalton133944a2018-11-16 23:30:29 -0500257 this->writeInstanceData(fLocalRect);
Chris Dalton133944a2018-11-16 23:30:29 -0500258 }
259 fInstanceStride = fInstanceData.count();
260
Chris Dalton4b62aed2019-01-15 11:53:00 -0700261 return analysis;
Chris Dalton133944a2018-11-16 23:30:29 -0500262}
263
Herb Derbye25c3002020-10-27 15:57:27 -0400264GrOp::CombineResult FillRRectOp::onCombineIfPossible(GrOp* op, SkArenaAlloc*, const GrCaps& caps) {
Robert Phillips366176b2020-02-26 11:40:50 -0500265 const auto& that = *op->cast<FillRRectOp>();
Robert Phillips360ec182020-03-26 13:29:50 -0400266 if (!fHelper.isCompatible(that.fHelper, caps, this->bounds(), that.bounds())) {
267 return CombineResult::kCannotCombine;
268 }
269
270 if (fProcessorFlags != that.fProcessorFlags ||
Chris Dalton133944a2018-11-16 23:30:29 -0500271 fInstanceData.count() > std::numeric_limits<int>::max() - that.fInstanceData.count()) {
272 return CombineResult::kCannotCombine;
273 }
274
275 fInstanceData.push_back_n(that.fInstanceData.count(), that.fInstanceData.begin());
276 fInstanceCount += that.fInstanceCount;
277 SkASSERT(fInstanceStride == that.fInstanceStride);
278 return CombineResult::kMerged;
279}
280
Robert Phillips366176b2020-02-26 11:40:50 -0500281class FillRRectOp::Processor : public GrGeometryProcessor {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600282public:
Robert Phillips360ec182020-03-26 13:29:50 -0400283 static GrGeometryProcessor* Make(SkArenaAlloc* arena, GrAAType aaType, ProcessorFlags flags) {
Mike Kleinf1241082020-12-14 15:59:09 -0600284 return arena->make([&](void* ptr) {
285 return new (ptr) Processor(aaType, flags);
286 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500287 }
288
Robert Phillips8053c972019-11-21 10:44:53 -0500289 const char* name() const final { return "GrFillRRectOp::Processor"; }
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500290
Robert Phillips8053c972019-11-21 10:44:53 -0500291 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const final {
Chris Daltoncc13b352021-03-05 14:59:01 -0700292 b->addBits(kNumProcessorFlags, (uint32_t)fFlags, "flags");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500293 }
294
Robert Phillipsf10535f2021-03-23 09:30:45 -0400295 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500296
297private:
Robert Phillips360ec182020-03-26 13:29:50 -0400298 Processor(GrAAType aaType, ProcessorFlags flags)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500299 : INHERITED(kGrFillRRectOp_Processor_ClassID)
Chris Dalton0dffbab2019-03-27 13:08:50 -0600300 , fFlags(flags) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700301 this->setVertexAttributes(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
Chris Dalton133944a2018-11-16 23:30:29 -0500302
Chris Daltoncc13b352021-03-05 14:59:01 -0700303 fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
304 fInstanceAttribs.emplace_back("translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600305 fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
306 fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
307 fColorAttrib = &fInstanceAttribs.push_back(
Robert Phillips360ec182020-03-26 13:29:50 -0400308 MakeColorAttribute("color", (fFlags & ProcessorFlags::kWideColor)));
309 if (fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600310 fInstanceAttribs.emplace_back(
311 "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
312 }
313 this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
Chris Dalton0dffbab2019-03-27 13:08:50 -0600314 }
315
Chris Dalton0dffbab2019-03-27 13:08:50 -0600316 static constexpr Attribute kVertexAttribs[] = {
317 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
318 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
319 // Coverage only.
320 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
321
Robert Phillips360ec182020-03-26 13:29:50 -0400322 const ProcessorFlags fFlags;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600323
324 SkSTArray<6, Attribute> fInstanceAttribs;
325 const Attribute* fColorAttrib;
326
Chris Daltoncc13b352021-03-05 14:59:01 -0700327 class Impl;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500328
John Stiles7571f9e2020-09-02 22:42:33 -0400329 using INHERITED = GrGeometryProcessor;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600330};
331
Robert Phillips787fd9d2021-03-22 14:48:09 -0400332constexpr GrGeometryProcessor::Attribute FillRRectOp::Processor::kVertexAttribs[];
Chris Dalton0dffbab2019-03-27 13:08:50 -0600333
334// Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
Chris Dalton133944a2018-11-16 23:30:29 -0500335// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
336// edges. The Vertex struct tells the shader where to place its vertex within a normalized
337// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600338struct CoverageVertex {
Chris Dalton133944a2018-11-16 23:30:29 -0500339 std::array<float, 4> fRadiiSelector;
340 std::array<float, 2> fCorner;
341 std::array<float, 2> fRadiusOutset;
342 std::array<float, 2> fAABloatDirection;
343 float fCoverage;
344 float fIsLinearCoverage;
Chris Dalton133944a2018-11-16 23:30:29 -0500345};
346
347// This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
348// of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
349// rectangles.
350static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
351
Chris Daltoncc13b352021-03-05 14:59:01 -0700352static constexpr CoverageVertex kVertexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500353 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700354 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
355 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500356
357 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700358 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
359 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500360
361 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700362 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
363 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500364
365 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700366 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
367 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500368
369
370 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700371 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
372 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500373
374 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700375 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
376 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500377
378 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700379 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
380 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500381
382 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700383 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
384 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500385
386
387 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700388 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
389 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
390 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
391 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
392 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
393 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500394
395 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700396 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
397 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
398 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
399 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
400 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
401 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500402
403 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700404 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
405 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
406 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
407 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
408 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
409 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500410
411 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700412 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
413 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
414 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
415 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
Chris Dalton2d07e862018-11-26 12:30:47 -0700416 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700417 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500418
Chris Daltoncc13b352021-03-05 14:59:01 -0700419GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500420
Chris Daltoncc13b352021-03-05 14:59:01 -0700421static constexpr uint16_t kIndexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500422 // Inset octagon (solid coverage).
423 0, 1, 7,
424 1, 2, 7,
425 7, 2, 6,
426 2, 3, 6,
427 6, 3, 5,
428 3, 4, 5,
429
430 // AA borders (linear coverage).
431 0, 1, 8, 1, 9, 8,
432 2, 3, 10, 3, 11, 10,
433 4, 5, 12, 5, 13, 12,
434 6, 7, 14, 7, 15, 14,
435
436 // Top-left arc.
437 16, 17, 21,
438 17, 21, 18,
439 21, 18, 20,
440 18, 20, 19,
441
442 // Top-right arc.
443 22, 23, 27,
444 23, 27, 24,
445 27, 24, 26,
446 24, 26, 25,
447
448 // Bottom-right arc.
449 28, 29, 33,
450 29, 33, 30,
451 33, 30, 32,
452 30, 32, 31,
453
454 // Bottom-left arc.
455 34, 35, 39,
456 35, 39, 36,
457 39, 36, 38,
458 36, 38, 37};
459
Chris Daltoncc13b352021-03-05 14:59:01 -0700460GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400461
Robert Phillipscad8fba2020-03-20 15:39:29 -0400462void FillRRectOp::onPrepareDraws(Target* target) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700463 // We request no multisample, but some platforms don't support disabling it on MSAA targets.
464 if (target->rtProxy()->numSamples() > 1 && !target->caps().multisampleDisableSupport()) {
465 fProcessorFlags |= ProcessorFlags::kMSAAEnabled;
466 }
467
Robert Phillipscad8fba2020-03-20 15:39:29 -0400468 if (void* instanceData = target->makeVertexSpace(fInstanceStride, fInstanceCount,
469 &fInstanceBuffer, &fBaseInstance)) {
Greg Danielf793de12019-09-05 13:23:23 -0400470 SkASSERT(fInstanceStride * fInstanceCount == fInstanceData.count());
471 memcpy(instanceData, fInstanceData.begin(), fInstanceData.count());
472 }
473
Chris Daltoncc13b352021-03-05 14:59:01 -0700474 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400475
Chris Daltoncc13b352021-03-05 14:59:01 -0700476 fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kIndex,
477 sizeof(kIndexData),
478 kIndexData, gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400479
Chris Daltoncc13b352021-03-05 14:59:01 -0700480 GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400481
Chris Daltoncc13b352021-03-05 14:59:01 -0700482 fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,
483 sizeof(kVertexData),
484 kVertexData,
485 gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400486}
487
Chris Daltoncc13b352021-03-05 14:59:01 -0700488class FillRRectOp::Processor::Impl : public GrGLSLGeometryProcessor {
Chris Dalton133944a2018-11-16 23:30:29 -0500489 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
John Stiles4d7ac492021-03-09 20:16:43 -0500490 GrGLSLVertexBuilder* v = args.fVertBuilder;
491 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
492
Robert Phillips787fd9d2021-03-22 14:48:09 -0400493 const auto& proc = args.fGeomProc.cast<Processor>();
Robert Phillips360ec182020-03-26 13:29:50 -0400494 bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
Chris Dalton133944a2018-11-16 23:30:29 -0500495
Chris Dalton0dffbab2019-03-27 13:08:50 -0600496 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
497
Chris Dalton133944a2018-11-16 23:30:29 -0500498 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
499 varyings->emitAttributes(proc);
John Stiles4d7ac492021-03-09 20:16:43 -0500500 f->codeAppendf("half4 %s;", args.fOutputColor);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600501 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500502 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
503
504 // Emit the vertex shader.
Chris Daltoncc13b352021-03-05 14:59:01 -0700505 // When MSAA is enabled, we need to make sure every sample gets lit up on pixels that have
506 // fractional coverage. We do this by making the ramp wider.
507 v->codeAppendf("float aa_bloat_multiplier = %i;",
508 (proc.fFlags & ProcessorFlags::kMSAAEnabled)
509 ? 2 // Outset an entire pixel (2 radii).
510 : (!(proc.fFlags & ProcessorFlags::kFakeNonAA))
511 ? 1 // Outset one half pixel (1 radius).
512 : 0); // No AA bloat.
513
Chris Dalton133944a2018-11-16 23:30:29 -0500514 // Unpack vertex attribs.
515 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
516 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
517 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
Chris Dalton133944a2018-11-16 23:30:29 -0500518 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
519
520 // Find the amount to bloat each edge for AA (in source space).
521 v->codeAppend("float2 pixellength = inversesqrt("
522 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
523 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
524 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
525 "abs(normalized_axis_dirs.zw));");
526 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
527
528 // Identify our radii.
Mike Reedd3efa992018-11-28 13:13:15 +0000529 v->codeAppend("float4 radii_and_neighbors = radii_selector"
530 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
531 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
532 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500533
Chris Daltoncc13b352021-03-05 14:59:01 -0700534 v->codeAppend("float coverage_multiplier = 1;");
Chris Dalton133944a2018-11-16 23:30:29 -0500535 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700536 // The rrect is more narrow than a half-pixel AA coverage ramp. We can't
537 // draw as-is or else opposite AA borders will overlap. Instead, fudge the
538 // size up to the width of a coverage ramp, and then reduce total coverage
539 // to make the rect appear more thin.
Chris Dalton133944a2018-11-16 23:30:29 -0500540 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
Chris Daltoncc13b352021-03-05 14:59:01 -0700541 v->codeAppend( "coverage_multiplier = 1 / (max(aa_bloatradius.x, 1) * "
542 "max(aa_bloatradius.y, 1));");
Chris Dalton133944a2018-11-16 23:30:29 -0500543 // Set radii to zero to ensure we take the "linear coverage" codepath.
544 // (The "coverage" variable only has effect in the linear codepath.)
545 v->codeAppend( "radii = float2(0);");
546 v->codeAppend("}");
547
Chris Daltoncc13b352021-03-05 14:59:01 -0700548 // Unpack coverage.
549 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
550 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
551 // MSAA has a wider ramp that goes from -.5 to 1.5 instead of 0 to 1.
552 v->codeAppendf("coverage = (coverage - .5) * aa_bloat_multiplier + .5;");
553 }
554
555 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.5))) {");
Chris Dalton133944a2018-11-16 23:30:29 -0500556 // The radii are very small. Demote this arc to a sharp 90 degree corner.
Chris Daltoncc13b352021-03-05 14:59:01 -0700557 v->codeAppend( "radii = float2(0);");
558 // Convert to a standard picture frame for an AA rect instead of the round
559 // rect geometry.
560 v->codeAppend( "aa_bloat_direction = sign(corner);");
561 v->codeAppend( "if (coverage > .5) {"); // Are we an inset edge?
562 v->codeAppend( "aa_bloat_direction = -aa_bloat_direction;");
563 v->codeAppend( "}");
Chris Dalton133944a2018-11-16 23:30:29 -0500564 v->codeAppend( "is_linear_coverage = 1;");
565 v->codeAppend("} else {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700566 // Don't let radii get smaller than a coverage ramp plus an extra half
567 // pixel for MSAA. Always use the same amount so we don't pop when
568 // switching between MSAA and coverage.
569 v->codeAppend( "radii = clamp(radii, pixellength * 1.5, 2 - pixellength * 1.5);");
570 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength * 1.5, "
571 "2 - pixellength * 1.5);");
Mike Reedd3efa992018-11-28 13:13:15 +0000572 // Don't let neighboring radii get closer together than 1/16 pixel.
573 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
574 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
575 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500576 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500577
578 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
579 // normalized [-1,-1,+1,+1] space.
Chris Daltoncc13b352021-03-05 14:59:01 -0700580 v->codeAppend("float2 aa_outset = "
581 "aa_bloat_direction * aa_bloatradius * aa_bloat_multiplier;");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700582 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500583
Chris Daltoncc13b352021-03-05 14:59:01 -0700584 v->codeAppend("if (coverage > .5) {"); // Are we an inset edge?
585 // Don't allow the aa insets to overlap. i.e., Don't let them inset past
586 // the center (x=y=0). Since we don't allow the rect to become thinner
587 // than 1px, this should only happen when using MSAA, where we inset by an
588 // entire pixel instead of half.
589 v->codeAppend( "if (aa_bloat_direction.x != 0 && vertexpos.x * corner.x < 0) {");
590 v->codeAppend( "float backset = abs(vertexpos.x);");
591 v->codeAppend( "vertexpos.x = 0;");
592 v->codeAppend( "vertexpos.y += "
593 "backset * sign(corner.y) * pixellength.y/pixellength.x;");
594 v->codeAppend( "coverage = (coverage - .5) * abs(corner.x) / "
595 "(abs(corner.x) + backset) + .5;");
596 v->codeAppend( "}");
597 v->codeAppend( "if (aa_bloat_direction.y != 0 && vertexpos.y * corner.y < 0) {");
598 v->codeAppend( "float backset = abs(vertexpos.y);");
599 v->codeAppend( "vertexpos.y = 0;");
600 v->codeAppend( "vertexpos.x += "
601 "backset * sign(corner.x) * pixellength.x/pixellength.y;");
602 v->codeAppend( "coverage = (coverage - .5) * abs(corner.y) / "
603 "(abs(corner.y) + backset) + .5;");
604 v->codeAppend( "}");
605 v->codeAppend("}");
606
Michael Ludwig553db622020-06-19 10:47:30 -0400607 // Write positions
Chris Dalton133944a2018-11-16 23:30:29 -0500608 GrShaderVar localCoord("", kFloat2_GrSLType);
Robert Phillips360ec182020-03-26 13:29:50 -0400609 if (proc.fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton133944a2018-11-16 23:30:29 -0500610 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
611 "local_rect.zw * (1 + vertexpos)) * .5;");
Michael Ludwig553db622020-06-19 10:47:30 -0400612 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
Chris Dalton133944a2018-11-16 23:30:29 -0500613 }
Chris Dalton133944a2018-11-16 23:30:29 -0500614
615 // Transform to device space.
616 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
617 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
618 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
619
620 // Setup interpolants for coverage.
621 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
622 varyings->addVarying("arccoord", &arcCoord);
623 v->codeAppend("if (0 != is_linear_coverage) {");
624 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
625 // interpolate linear coverage across y.
Chris Daltoncc13b352021-03-05 14:59:01 -0700626 v->codeAppendf( "%s.xy = float2(0, coverage * coverage_multiplier);",
627 arcCoord.vsOut());
Chris Dalton133944a2018-11-16 23:30:29 -0500628 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700629 // Find the normalized arc coordinates for our corner ellipse.
630 // (i.e., the coordinate system where x^2 + y^2 == 1).
631 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500632 // We are a corner piece: Interpolate the arc coordinates for coverage.
633 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
634 // instructs the fragment shader to use linear coverage).
635 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
636 if (!useHWDerivatives) {
637 // The gradient is order-1: Interpolate it across arccoord.zw.
638 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
639 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
640 }
641 v->codeAppend("}");
642
643 // Emit the fragment shader.
Chris Dalton133944a2018-11-16 23:30:29 -0500644 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
645 f->codeAppendf("half coverage;");
646 f->codeAppendf("if (0 == x_plus_1) {");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600647 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500648 f->codeAppendf("} else {");
649 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
650 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
651 if (useHWDerivatives) {
652 f->codeAppendf("float fnwidth = fwidth(fn);");
653 } else {
654 // The gradient is interpolated across arccoord.zw.
655 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
656 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
657 }
Chris Daltoncc13b352021-03-05 14:59:01 -0700658 f->codeAppendf( "coverage = .5 - half(fn/fnwidth);");
659 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
660 // MSAA uses ramps larger than 1px, so we need to clamp in both branches.
661 f->codeAppendf("}");
662 }
663 f->codeAppendf("coverage = clamp(coverage, 0, 1);");
664 if (!(proc.fFlags & ProcessorFlags::kMSAAEnabled)) {
665 // When not using MSAA, we only need to clamp in the "arc" branch.
666 f->codeAppendf("}");
667 }
668 if (proc.fFlags & ProcessorFlags::kFakeNonAA) {
669 f->codeAppendf("coverage = (coverage >= .5) ? 1 : 0;");
670 }
John Stiles4d7ac492021-03-09 20:16:43 -0500671 f->codeAppendf("half4 %s = half4(coverage);", args.fOutputCoverage);
Chris Dalton133944a2018-11-16 23:30:29 -0500672 }
673
Robert Phillips787fd9d2021-03-22 14:48:09 -0400674 void setData(const GrGLSLProgramDataManager&, 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,
Greg Danield358cbe2020-09-11 09:33:54 -0400686 const GrXferProcessor::DstProxyView& 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