blob: 32fd16471bfd85d7f87edc9fcdb71fddf5ee1f09 [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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
20#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
21#include "src/gpu/glsl/GrGLSLVarying.h"
22#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
Robert Phillipscad8fba2020-03-20 15:39:29 -040023#include "src/gpu/ops/GrMeshDrawOp.h"
Robert Phillipsce978572020-02-28 11:56:44 -050024#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
Robert Phillips366176b2020-02-26 11:40:50 -050025
26namespace {
27
Robert Phillipscad8fba2020-03-20 15:39:29 -040028class FillRRectOp : public GrMeshDrawOp {
Robert Phillips360ec182020-03-26 13:29:50 -040029private:
30 using Helper = GrSimpleMeshDrawOpHelper;
31
Robert Phillips366176b2020-02-26 11:40:50 -050032public:
33 DEFINE_OP_CLASS_ID
34
Herb Derbyc76d4092020-10-07 16:46:15 -040035 static GrOp::Owner Make(GrRecordingContext*,
36 GrPaint&&,
37 const SkMatrix& viewMatrix,
38 const SkRRect&,
Chris Dalton61d694d2021-03-22 00:00:52 -060039 const SkRect& localRect,
Chris Dalton4f447342021-03-12 12:09:12 -070040 GrAA);
Robert Phillips366176b2020-02-26 11:40:50 -050041
42 const char* name() const final { return "GrFillRRectOp"; }
43
Robert Phillips360ec182020-03-26 13:29:50 -040044 FixedFunctionFlags fixedFunctionFlags() const final { return fHelper.fixedFunctionFlags(); }
45
Chris Dalton57ab06c2021-04-22 12:57:28 -060046 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, 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 Phillips294723d2021-06-17 09:23:58 -040049 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips366176b2020-02-26 11:40:50 -050050 if (fProgramInfo) {
Robert Phillips294723d2021-06-17 09:23:58 -040051 fProgramInfo->visitFPProxies(func);
Robert Phillips366176b2020-02-26 11:40:50 -050052 } else {
Robert Phillips294723d2021-06-17 09:23:58 -040053 fHelper.visitProxies(func);
Robert Phillips366176b2020-02-26 11:40:50 -050054 }
55 }
56
Robert Phillips71143952021-06-17 14:55:07 -040057 void onPrepareDraws(GrMeshDrawTarget*) 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&&,
John Stiles52cb1d02021-06-02 11:58:05 -0400110 const GrDstProxyView&,
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
Chris Dalton57ab06c2021-04-22 12:57:28 -0600237GrProcessorSet::Analysis FillRRectOp::finalize(const GrCaps& caps, const GrAppliedClip* clip,
238 GrClampType clampType) {
Chris Dalton133944a2018-11-16 23:30:29 -0500239 SkASSERT(1 == fInstanceCount);
240
Robert Phillips360ec182020-03-26 13:29:50 -0400241 bool isWideColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -0600242 auto analysis = fHelper.finalizeProcessors(caps, clip, clampType,
243 GrProcessorAnalysisCoverage::kSingleChannel, &fColor,
244 &isWideColor);
Chris Dalton133944a2018-11-16 23:30:29 -0500245
246 // Finish writing the instance attribs.
Robert Phillips360ec182020-03-26 13:29:50 -0400247 if (isWideColor) {
248 fProcessorFlags |= ProcessorFlags::kWideColor;
249 this->writeInstanceData(fColor);
Brian Osman5105d682019-02-13 16:06:14 -0500250 } else {
Robert Phillips360ec182020-03-26 13:29:50 -0400251 this->writeInstanceData(fColor.toBytes_RGBA());
Brian Osman5105d682019-02-13 16:06:14 -0500252 }
253
Chris Dalton133944a2018-11-16 23:30:29 -0500254 if (analysis.usesLocalCoords()) {
Robert Phillips360ec182020-03-26 13:29:50 -0400255 fProcessorFlags |= ProcessorFlags::kHasLocalCoords;
Chris Dalton133944a2018-11-16 23:30:29 -0500256 this->writeInstanceData(fLocalRect);
Chris Dalton133944a2018-11-16 23:30:29 -0500257 }
258 fInstanceStride = fInstanceData.count();
259
Chris Dalton4b62aed2019-01-15 11:53:00 -0700260 return analysis;
Chris Dalton133944a2018-11-16 23:30:29 -0500261}
262
Herb Derbye25c3002020-10-27 15:57:27 -0400263GrOp::CombineResult FillRRectOp::onCombineIfPossible(GrOp* op, SkArenaAlloc*, const GrCaps& caps) {
Robert Phillips366176b2020-02-26 11:40:50 -0500264 const auto& that = *op->cast<FillRRectOp>();
Robert Phillips360ec182020-03-26 13:29:50 -0400265 if (!fHelper.isCompatible(that.fHelper, caps, this->bounds(), that.bounds())) {
266 return CombineResult::kCannotCombine;
267 }
268
269 if (fProcessorFlags != that.fProcessorFlags ||
Chris Dalton133944a2018-11-16 23:30:29 -0500270 fInstanceData.count() > std::numeric_limits<int>::max() - that.fInstanceData.count()) {
271 return CombineResult::kCannotCombine;
272 }
273
274 fInstanceData.push_back_n(that.fInstanceData.count(), that.fInstanceData.begin());
275 fInstanceCount += that.fInstanceCount;
276 SkASSERT(fInstanceStride == that.fInstanceStride);
277 return CombineResult::kMerged;
278}
279
Robert Phillips366176b2020-02-26 11:40:50 -0500280class FillRRectOp::Processor : public GrGeometryProcessor {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600281public:
Robert Phillips360ec182020-03-26 13:29:50 -0400282 static GrGeometryProcessor* Make(SkArenaAlloc* arena, GrAAType aaType, ProcessorFlags flags) {
Mike Kleinf1241082020-12-14 15:59:09 -0600283 return arena->make([&](void* ptr) {
284 return new (ptr) Processor(aaType, flags);
285 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500286 }
287
Robert Phillips8053c972019-11-21 10:44:53 -0500288 const char* name() const final { return "GrFillRRectOp::Processor"; }
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500289
Robert Phillips8053c972019-11-21 10:44:53 -0500290 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const final {
Chris Daltoncc13b352021-03-05 14:59:01 -0700291 b->addBits(kNumProcessorFlags, (uint32_t)fFlags, "flags");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500292 }
293
Robert Phillipsf10535f2021-03-23 09:30:45 -0400294 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500295
296private:
Robert Phillips360ec182020-03-26 13:29:50 -0400297 Processor(GrAAType aaType, ProcessorFlags flags)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500298 : INHERITED(kGrFillRRectOp_Processor_ClassID)
Chris Dalton0dffbab2019-03-27 13:08:50 -0600299 , fFlags(flags) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700300 this->setVertexAttributes(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
Chris Dalton133944a2018-11-16 23:30:29 -0500301
Chris Daltoncc13b352021-03-05 14:59:01 -0700302 fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
303 fInstanceAttribs.emplace_back("translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600304 fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
305 fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
306 fColorAttrib = &fInstanceAttribs.push_back(
Robert Phillips360ec182020-03-26 13:29:50 -0400307 MakeColorAttribute("color", (fFlags & ProcessorFlags::kWideColor)));
308 if (fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600309 fInstanceAttribs.emplace_back(
310 "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
311 }
312 this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
Chris Dalton0dffbab2019-03-27 13:08:50 -0600313 }
314
Chris Dalton0dffbab2019-03-27 13:08:50 -0600315 static constexpr Attribute kVertexAttribs[] = {
316 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
317 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
318 // Coverage only.
319 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
320
Robert Phillips360ec182020-03-26 13:29:50 -0400321 const ProcessorFlags fFlags;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600322
323 SkSTArray<6, Attribute> fInstanceAttribs;
324 const Attribute* fColorAttrib;
325
Chris Daltoncc13b352021-03-05 14:59:01 -0700326 class Impl;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500327
John Stiles7571f9e2020-09-02 22:42:33 -0400328 using INHERITED = GrGeometryProcessor;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600329};
330
Robert Phillips787fd9d2021-03-22 14:48:09 -0400331constexpr GrGeometryProcessor::Attribute FillRRectOp::Processor::kVertexAttribs[];
Chris Dalton0dffbab2019-03-27 13:08:50 -0600332
333// Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
Chris Dalton133944a2018-11-16 23:30:29 -0500334// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
335// edges. The Vertex struct tells the shader where to place its vertex within a normalized
336// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600337struct CoverageVertex {
Chris Dalton133944a2018-11-16 23:30:29 -0500338 std::array<float, 4> fRadiiSelector;
339 std::array<float, 2> fCorner;
340 std::array<float, 2> fRadiusOutset;
341 std::array<float, 2> fAABloatDirection;
342 float fCoverage;
343 float fIsLinearCoverage;
Chris Dalton133944a2018-11-16 23:30:29 -0500344};
345
346// This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
347// of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
348// rectangles.
349static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
350
Chris Daltoncc13b352021-03-05 14:59:01 -0700351static constexpr CoverageVertex kVertexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500352 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700353 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
354 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500355
356 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700357 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
358 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500359
360 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700361 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
362 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500363
364 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700365 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
366 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500367
368
369 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700370 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
371 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500372
373 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700374 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
375 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500376
377 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700378 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
379 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500380
381 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700382 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
383 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500384
385
386 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700387 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
388 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
389 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
390 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
391 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
392 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500393
394 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700395 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
396 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
397 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
398 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
399 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
400 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500401
402 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700403 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
404 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
405 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
406 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
407 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
408 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500409
410 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700411 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
412 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
413 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
414 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
Chris Dalton2d07e862018-11-26 12:30:47 -0700415 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700416 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500417
Chris Daltoncc13b352021-03-05 14:59:01 -0700418GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500419
Chris Daltoncc13b352021-03-05 14:59:01 -0700420static constexpr uint16_t kIndexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500421 // Inset octagon (solid coverage).
422 0, 1, 7,
423 1, 2, 7,
424 7, 2, 6,
425 2, 3, 6,
426 6, 3, 5,
427 3, 4, 5,
428
429 // AA borders (linear coverage).
430 0, 1, 8, 1, 9, 8,
431 2, 3, 10, 3, 11, 10,
432 4, 5, 12, 5, 13, 12,
433 6, 7, 14, 7, 15, 14,
434
435 // Top-left arc.
436 16, 17, 21,
437 17, 21, 18,
438 21, 18, 20,
439 18, 20, 19,
440
441 // Top-right arc.
442 22, 23, 27,
443 23, 27, 24,
444 27, 24, 26,
445 24, 26, 25,
446
447 // Bottom-right arc.
448 28, 29, 33,
449 29, 33, 30,
450 33, 30, 32,
451 30, 32, 31,
452
453 // Bottom-left arc.
454 34, 35, 39,
455 35, 39, 36,
456 39, 36, 38,
457 36, 38, 37};
458
Chris Daltoncc13b352021-03-05 14:59:01 -0700459GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400460
Robert Phillips71143952021-06-17 14:55:07 -0400461void FillRRectOp::onPrepareDraws(GrMeshDrawTarget* target) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700462 // We request no multisample, but some platforms don't support disabling it on MSAA targets.
Chris Dalton2517ce32021-04-13 00:21:15 -0600463 if (target->usesMSAASurface() && !target->caps().multisampleDisableSupport()) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700464 fProcessorFlags |= ProcessorFlags::kMSAAEnabled;
465 }
466
Robert Phillipscad8fba2020-03-20 15:39:29 -0400467 if (void* instanceData = target->makeVertexSpace(fInstanceStride, fInstanceCount,
468 &fInstanceBuffer, &fBaseInstance)) {
Greg Danielf793de12019-09-05 13:23:23 -0400469 SkASSERT(fInstanceStride * fInstanceCount == fInstanceData.count());
470 memcpy(instanceData, fInstanceData.begin(), fInstanceData.count());
471 }
472
Chris Daltoncc13b352021-03-05 14:59:01 -0700473 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400474
Chris Daltoncc13b352021-03-05 14:59:01 -0700475 fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kIndex,
476 sizeof(kIndexData),
477 kIndexData, gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400478
Chris Daltoncc13b352021-03-05 14:59:01 -0700479 GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400480
Chris Daltoncc13b352021-03-05 14:59:01 -0700481 fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,
482 sizeof(kVertexData),
483 kVertexData,
484 gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400485}
486
Chris Daltoncc13b352021-03-05 14:59:01 -0700487class FillRRectOp::Processor::Impl : public GrGLSLGeometryProcessor {
Chris Dalton133944a2018-11-16 23:30:29 -0500488 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
John Stiles4d7ac492021-03-09 20:16:43 -0500489 GrGLSLVertexBuilder* v = args.fVertBuilder;
490 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
491
Robert Phillips787fd9d2021-03-22 14:48:09 -0400492 const auto& proc = args.fGeomProc.cast<Processor>();
Robert Phillips360ec182020-03-26 13:29:50 -0400493 bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
Chris Dalton133944a2018-11-16 23:30:29 -0500494
Chris Dalton0dffbab2019-03-27 13:08:50 -0600495 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
496
Chris Dalton133944a2018-11-16 23:30:29 -0500497 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
498 varyings->emitAttributes(proc);
John Stiles4d7ac492021-03-09 20:16:43 -0500499 f->codeAppendf("half4 %s;", args.fOutputColor);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600500 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500501 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
502
503 // Emit the vertex shader.
Chris Daltoncc13b352021-03-05 14:59:01 -0700504 // When MSAA is enabled, we need to make sure every sample gets lit up on pixels that have
505 // fractional coverage. We do this by making the ramp wider.
506 v->codeAppendf("float aa_bloat_multiplier = %i;",
507 (proc.fFlags & ProcessorFlags::kMSAAEnabled)
508 ? 2 // Outset an entire pixel (2 radii).
509 : (!(proc.fFlags & ProcessorFlags::kFakeNonAA))
510 ? 1 // Outset one half pixel (1 radius).
511 : 0); // No AA bloat.
512
Chris Dalton133944a2018-11-16 23:30:29 -0500513 // Unpack vertex attribs.
514 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
515 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
516 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
Chris Dalton133944a2018-11-16 23:30:29 -0500517 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
518
519 // Find the amount to bloat each edge for AA (in source space).
520 v->codeAppend("float2 pixellength = inversesqrt("
521 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
522 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
523 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
524 "abs(normalized_axis_dirs.zw));");
525 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
526
527 // Identify our radii.
Mike Reedd3efa992018-11-28 13:13:15 +0000528 v->codeAppend("float4 radii_and_neighbors = radii_selector"
529 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
530 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
531 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500532
Chris Daltoncc13b352021-03-05 14:59:01 -0700533 v->codeAppend("float coverage_multiplier = 1;");
Chris Dalton133944a2018-11-16 23:30:29 -0500534 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700535 // The rrect is more narrow than a half-pixel AA coverage ramp. We can't
536 // draw as-is or else opposite AA borders will overlap. Instead, fudge the
537 // size up to the width of a coverage ramp, and then reduce total coverage
538 // to make the rect appear more thin.
Chris Dalton133944a2018-11-16 23:30:29 -0500539 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
Chris Daltoncc13b352021-03-05 14:59:01 -0700540 v->codeAppend( "coverage_multiplier = 1 / (max(aa_bloatradius.x, 1) * "
541 "max(aa_bloatradius.y, 1));");
Chris Dalton133944a2018-11-16 23:30:29 -0500542 // Set radii to zero to ensure we take the "linear coverage" codepath.
543 // (The "coverage" variable only has effect in the linear codepath.)
544 v->codeAppend( "radii = float2(0);");
545 v->codeAppend("}");
546
Chris Daltoncc13b352021-03-05 14:59:01 -0700547 // Unpack coverage.
548 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
549 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
550 // MSAA has a wider ramp that goes from -.5 to 1.5 instead of 0 to 1.
551 v->codeAppendf("coverage = (coverage - .5) * aa_bloat_multiplier + .5;");
552 }
553
554 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.5))) {");
Chris Dalton133944a2018-11-16 23:30:29 -0500555 // The radii are very small. Demote this arc to a sharp 90 degree corner.
Chris Daltoncc13b352021-03-05 14:59:01 -0700556 v->codeAppend( "radii = float2(0);");
557 // Convert to a standard picture frame for an AA rect instead of the round
558 // rect geometry.
559 v->codeAppend( "aa_bloat_direction = sign(corner);");
560 v->codeAppend( "if (coverage > .5) {"); // Are we an inset edge?
561 v->codeAppend( "aa_bloat_direction = -aa_bloat_direction;");
562 v->codeAppend( "}");
Chris Dalton133944a2018-11-16 23:30:29 -0500563 v->codeAppend( "is_linear_coverage = 1;");
564 v->codeAppend("} else {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700565 // Don't let radii get smaller than a coverage ramp plus an extra half
566 // pixel for MSAA. Always use the same amount so we don't pop when
567 // switching between MSAA and coverage.
568 v->codeAppend( "radii = clamp(radii, pixellength * 1.5, 2 - pixellength * 1.5);");
569 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength * 1.5, "
570 "2 - pixellength * 1.5);");
Mike Reedd3efa992018-11-28 13:13:15 +0000571 // Don't let neighboring radii get closer together than 1/16 pixel.
572 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
573 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
574 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500575 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500576
577 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
578 // normalized [-1,-1,+1,+1] space.
Chris Daltoncc13b352021-03-05 14:59:01 -0700579 v->codeAppend("float2 aa_outset = "
580 "aa_bloat_direction * aa_bloatradius * aa_bloat_multiplier;");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700581 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500582
Chris Daltoncc13b352021-03-05 14:59:01 -0700583 v->codeAppend("if (coverage > .5) {"); // Are we an inset edge?
584 // Don't allow the aa insets to overlap. i.e., Don't let them inset past
585 // the center (x=y=0). Since we don't allow the rect to become thinner
586 // than 1px, this should only happen when using MSAA, where we inset by an
587 // entire pixel instead of half.
588 v->codeAppend( "if (aa_bloat_direction.x != 0 && vertexpos.x * corner.x < 0) {");
589 v->codeAppend( "float backset = abs(vertexpos.x);");
590 v->codeAppend( "vertexpos.x = 0;");
591 v->codeAppend( "vertexpos.y += "
592 "backset * sign(corner.y) * pixellength.y/pixellength.x;");
593 v->codeAppend( "coverage = (coverage - .5) * abs(corner.x) / "
594 "(abs(corner.x) + backset) + .5;");
595 v->codeAppend( "}");
596 v->codeAppend( "if (aa_bloat_direction.y != 0 && vertexpos.y * corner.y < 0) {");
597 v->codeAppend( "float backset = abs(vertexpos.y);");
598 v->codeAppend( "vertexpos.y = 0;");
599 v->codeAppend( "vertexpos.x += "
600 "backset * sign(corner.x) * pixellength.x/pixellength.y;");
601 v->codeAppend( "coverage = (coverage - .5) * abs(corner.y) / "
602 "(abs(corner.y) + backset) + .5;");
603 v->codeAppend( "}");
604 v->codeAppend("}");
605
Michael Ludwig553db622020-06-19 10:47:30 -0400606 // Write positions
Chris Dalton133944a2018-11-16 23:30:29 -0500607 GrShaderVar localCoord("", kFloat2_GrSLType);
Robert Phillips360ec182020-03-26 13:29:50 -0400608 if (proc.fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton133944a2018-11-16 23:30:29 -0500609 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
610 "local_rect.zw * (1 + vertexpos)) * .5;");
Michael Ludwig553db622020-06-19 10:47:30 -0400611 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
Chris Dalton133944a2018-11-16 23:30:29 -0500612 }
Chris Dalton133944a2018-11-16 23:30:29 -0500613
614 // Transform to device space.
615 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
616 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
617 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
618
619 // Setup interpolants for coverage.
620 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
621 varyings->addVarying("arccoord", &arcCoord);
622 v->codeAppend("if (0 != is_linear_coverage) {");
623 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
624 // interpolate linear coverage across y.
Chris Daltoncc13b352021-03-05 14:59:01 -0700625 v->codeAppendf( "%s.xy = float2(0, coverage * coverage_multiplier);",
626 arcCoord.vsOut());
Chris Dalton133944a2018-11-16 23:30:29 -0500627 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700628 // Find the normalized arc coordinates for our corner ellipse.
629 // (i.e., the coordinate system where x^2 + y^2 == 1).
630 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500631 // We are a corner piece: Interpolate the arc coordinates for coverage.
632 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
633 // instructs the fragment shader to use linear coverage).
634 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
635 if (!useHWDerivatives) {
636 // The gradient is order-1: Interpolate it across arccoord.zw.
637 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
638 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
639 }
640 v->codeAppend("}");
641
642 // Emit the fragment shader.
Chris Dalton133944a2018-11-16 23:30:29 -0500643 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
644 f->codeAppendf("half coverage;");
645 f->codeAppendf("if (0 == x_plus_1) {");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600646 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500647 f->codeAppendf("} else {");
648 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
649 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
650 if (useHWDerivatives) {
651 f->codeAppendf("float fnwidth = fwidth(fn);");
652 } else {
653 // The gradient is interpolated across arccoord.zw.
654 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
655 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
656 }
Chris Daltoncc13b352021-03-05 14:59:01 -0700657 f->codeAppendf( "coverage = .5 - half(fn/fnwidth);");
658 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
659 // MSAA uses ramps larger than 1px, so we need to clamp in both branches.
660 f->codeAppendf("}");
661 }
662 f->codeAppendf("coverage = clamp(coverage, 0, 1);");
663 if (!(proc.fFlags & ProcessorFlags::kMSAAEnabled)) {
664 // When not using MSAA, we only need to clamp in the "arc" branch.
665 f->codeAppendf("}");
666 }
667 if (proc.fFlags & ProcessorFlags::kFakeNonAA) {
668 f->codeAppendf("coverage = (coverage >= .5) ? 1 : 0;");
669 }
John Stiles4d7ac492021-03-09 20:16:43 -0500670 f->codeAppendf("half4 %s = half4(coverage);", args.fOutputCoverage);
Chris Dalton133944a2018-11-16 23:30:29 -0500671 }
672
Brian Salomon5a328282021-04-14 10:32:25 -0400673 void setData(const GrGLSLProgramDataManager&,
674 const GrShaderCaps&,
675 const GrGeometryProcessor&) override {}
Chris Dalton133944a2018-11-16 23:30:29 -0500676};
677
Chris Dalton0dffbab2019-03-27 13:08:50 -0600678
Robert Phillipsf10535f2021-03-23 09:30:45 -0400679GrGLSLGeometryProcessor* FillRRectOp::Processor::createGLSLInstance(const GrShaderCaps&) const {
Chris Daltoncc13b352021-03-05 14:59:01 -0700680 return new Impl();
Chris Dalton133944a2018-11-16 23:30:29 -0500681}
682
Robert Phillipscad8fba2020-03-20 15:39:29 -0400683void FillRRectOp::onCreateProgramInfo(const GrCaps* caps,
684 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -0500685 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400686 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -0400687 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500688 GrXferBarrierFlags renderPassXferBarriers,
689 GrLoadOp colorLoadOp) {
Robert Phillips360ec182020-03-26 13:29:50 -0400690 GrGeometryProcessor* gp = Processor::Make(arena, fHelper.aaType(), fProcessorFlags);
Robert Phillipsce978572020-02-28 11:56:44 -0500691 SkASSERT(gp->instanceStride() == (size_t)fInstanceStride);
Chris Dalton133944a2018-11-16 23:30:29 -0500692
Brian Salomon8afde5f2020-04-01 16:22:00 -0400693 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -0400694 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -0500695 renderPassXferBarriers, colorLoadOp);
Robert Phillips8053c972019-11-21 10:44:53 -0500696}
697
Robert Phillips366176b2020-02-26 11:40:50 -0500698void FillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Robert Phillips8053c972019-11-21 10:44:53 -0500699 if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
700 return; // Setup failed.
701 }
702
703 if (!fProgramInfo) {
Robert Phillipscad8fba2020-03-20 15:39:29 -0400704 this->createProgramInfo(flushState);
Robert Phillips8053c972019-11-21 10:44:53 -0500705 }
Robert Phillips901aff02019-10-08 12:32:56 -0400706
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600707 flushState->bindPipelineAndScissorClip(*fProgramInfo, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400708 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Greg Daniel426274b2020-07-20 11:37:38 -0400709 flushState->bindBuffers(std::move(fIndexBuffer), std::move(fInstanceBuffer),
710 std::move(fVertexBuffer));
Chris Daltoncc13b352021-03-05 14:59:01 -0700711 flushState->drawIndexedInstanced(SK_ARRAY_COUNT(kIndexData), 0, fInstanceCount, fBaseInstance,
712 0);
Chris Dalton133944a2018-11-16 23:30:29 -0500713}
714
715// Will the given corner look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600716static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
Chris Dalton133944a2018-11-16 23:30:29 -0500717 Sk2f devRadii = devScale * cornerRadii;
718 if (devRadii[1] < devRadii[0]) {
719 devRadii = SkNx_shuffle<1,0>(devRadii);
720 }
Brian Osman788b9162020-02-07 10:36:46 -0500721 float minDevRadius = std::max(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
Chris Dalton133944a2018-11-16 23:30:29 -0500722 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
723 // This threshold was arrived at subjevtively on an NVIDIA chip.
724 return minDevRadius * minDevRadius * 5 > devRadii[1];
725}
726
Chris Dalton0dffbab2019-03-27 13:08:50 -0600727static bool can_use_hw_derivatives_with_coverage(
728 const Sk2f& devScale, const SkVector& cornerRadii) {
729 return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
Chris Dalton133944a2018-11-16 23:30:29 -0500730}
731
732// Will the given round rect look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600733static bool can_use_hw_derivatives_with_coverage(
734 const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
Chris Dalton133944a2018-11-16 23:30:29 -0500735 if (!shaderCaps.shaderDerivativeSupport()) {
736 return false;
737 }
738
739 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
740 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
741 Sk2f devScale = (x*x + y*y).sqrt();
742 switch (rrect.getType()) {
743 case SkRRect::kEmpty_Type:
744 case SkRRect::kRect_Type:
745 return true;
746
747 case SkRRect::kOval_Type:
748 case SkRRect::kSimple_Type:
Chris Dalton0dffbab2019-03-27 13:08:50 -0600749 return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
Chris Dalton133944a2018-11-16 23:30:29 -0500750
751 case SkRRect::kNinePatch_Type: {
752 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
753 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
754 Sk2f minRadii = Sk2f::Min(r0, r1);
755 Sk2f maxRadii = Sk2f::Max(r0, r1);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600756 return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
757 can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
Chris Dalton133944a2018-11-16 23:30:29 -0500758 }
759
760 case SkRRect::kComplex_Type: {
761 for (int i = 0; i < 4; ++i) {
762 auto corner = static_cast<SkRRect::Corner>(i);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600763 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
Chris Dalton133944a2018-11-16 23:30:29 -0500764 return false;
765 }
766 }
767 return true;
768 }
769 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600770 SK_ABORT("Invalid round rect type.");
Chris Dalton133944a2018-11-16 23:30:29 -0500771}
Robert Phillips366176b2020-02-26 11:40:50 -0500772
773} // anonymous namespace
774
775
Herb Derbyc76d4092020-10-07 16:46:15 -0400776GrOp::Owner GrFillRRectOp::Make(GrRecordingContext* ctx,
777 GrPaint&& paint,
778 const SkMatrix& viewMatrix,
779 const SkRRect& rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600780 const SkRect& localRect,
Chris Dalton4f447342021-03-12 12:09:12 -0700781 GrAA aa) {
Chris Dalton61d694d2021-03-22 00:00:52 -0600782 return FillRRectOp::Make(ctx, std::move(paint), viewMatrix, rrect, localRect, aa);
Robert Phillips366176b2020-02-26 11:40:50 -0500783}
784
Chris Dalton61d694d2021-03-22 00:00:52 -0600785
Robert Phillips366176b2020-02-26 11:40:50 -0500786#if GR_TEST_UTILS
787
788#include "src/gpu/GrDrawOpTest.h"
789
790GR_DRAW_OP_TEST_DEFINE(FillRRectOp) {
Robert Phillips366176b2020-02-26 11:40:50 -0500791 SkMatrix viewMatrix = GrTest::TestMatrix(random);
Chris Dalton4f447342021-03-12 12:09:12 -0700792 GrAA aa = GrAA(random->nextBool());
Robert Phillips366176b2020-02-26 11:40:50 -0500793
794 SkRect rect = GrTest::TestRect(random);
795 float w = rect.width();
796 float h = rect.height();
797
798 SkRRect rrect;
799 // TODO: test out other rrect configurations
800 rrect.setNinePatch(rect, w / 3.0f, h / 4.0f, w / 5.0f, h / 6.0);
801
802 return GrFillRRectOp::Make(context,
Robert Phillips360ec182020-03-26 13:29:50 -0400803 std::move(paint),
Robert Phillips366176b2020-02-26 11:40:50 -0500804 viewMatrix,
805 rrect,
Chris Dalton61d694d2021-03-22 00:00:52 -0600806 rrect.rect(),
Chris Dalton4f447342021-03-12 12:09:12 -0700807 aa);
Robert Phillips366176b2020-02-26 11:40:50 -0500808}
809
810#endif