blob: ac5598793e6dedb4eeb615fe38fb1601304a5182 [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 Dalton4f447342021-03-12 12:09:12 -070038 GrAA);
Robert Phillips366176b2020-02-26 11:40:50 -050039
40 const char* name() const final { return "GrFillRRectOp"; }
41
Robert Phillips360ec182020-03-26 13:29:50 -040042 FixedFunctionFlags fixedFunctionFlags() const final { return fHelper.fixedFunctionFlags(); }
43
Robert Phillips366176b2020-02-26 11:40:50 -050044 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
45 bool hasMixedSampledCoverage, 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 Phillips366176b2020-02-26 11:40:50 -050048 void visitProxies(const VisitProxyFunc& fn) const override {
49 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -060050 fProgramInfo->visitFPProxies(fn);
Robert Phillips366176b2020-02-26 11:40:50 -050051 } else {
Robert Phillips360ec182020-03-26 13:29:50 -040052 fHelper.visitProxies(fn);
Robert Phillips366176b2020-02-26 11:40:50 -050053 }
54 }
55
Robert Phillipscad8fba2020-03-20 15:39:29 -040056 void onPrepareDraws(Target*) 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&,
Robert Phillips360ec182020-03-26 13:29:50 -040082 ProcessorFlags,
83 const SkRect& devBounds);
Robert Phillips366176b2020-02-26 11:40:50 -050084
85 // These methods are used to append data of various POD types to our internal array of instance
86 // data. The actual layout of the instance buffer can vary from Op to Op.
87 template <typename T> inline T* appendInstanceData(int count) {
88 static_assert(std::is_pod<T>::value, "");
89 static_assert(4 == alignof(T), "");
90 return reinterpret_cast<T*>(fInstanceData.push_back_n(sizeof(T) * count));
91 }
92
93 template <typename T, typename... Args>
94 inline void writeInstanceData(const T& val, const Args&... remainder) {
95 memcpy(this->appendInstanceData<T>(1), &val, sizeof(T));
96 this->writeInstanceData(remainder...);
97 }
98
99 void writeInstanceData() {} // Halt condition.
100
Robert Phillipscad8fba2020-03-20 15:39:29 -0400101 GrProgramInfo* programInfo() final { return fProgramInfo; }
102
Robert Phillips366176b2020-02-26 11:40:50 -0500103 // Create a GrProgramInfo object in the provided arena
Robert Phillipscad8fba2020-03-20 15:39:29 -0400104 void onCreateProgramInfo(const GrCaps*,
105 SkArenaAlloc*,
Adlai Hollere2296f72020-11-19 13:41:26 -0500106 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400107 GrAppliedClip&&,
Greg Danield358cbe2020-09-11 09:33:54 -0400108 const GrXferProcessor::DstProxyView&,
Greg Daniel42dbca52020-11-20 10:22:43 -0500109 GrXferBarrierFlags renderPassXferBarriers,
110 GrLoadOp colorLoadOp) final;
Robert Phillips366176b2020-02-26 11:40:50 -0500111
Robert Phillips360ec182020-03-26 13:29:50 -0400112 Helper fHelper;
113 SkPMColor4f fColor;
114 const SkRect fLocalRect;
115 ProcessorFlags fProcessorFlags;
Robert Phillips366176b2020-02-26 11:40:50 -0500116
117 SkSTArray<sizeof(float) * 16 * 4, char, /*MEM_MOVE=*/ true> fInstanceData;
118 int fInstanceCount = 1;
119 int fInstanceStride = 0;
120
121 sk_sp<const GrBuffer> fInstanceBuffer;
122 sk_sp<const GrBuffer> fVertexBuffer;
123 sk_sp<const GrBuffer> fIndexBuffer;
124 int fBaseInstance = 0;
Robert Phillips366176b2020-02-26 11:40:50 -0500125
126 // If this op is prePrepared the created programInfo will be stored here for use in
127 // onExecute. In the prePrepared case it will have been stored in the record-time arena.
128 GrProgramInfo* fProgramInfo = nullptr;
129
John Stiles7571f9e2020-09-02 22:42:33 -0400130 using INHERITED = GrMeshDrawOp;
Robert Phillips366176b2020-02-26 11:40:50 -0500131};
132
Robert Phillips360ec182020-03-26 13:29:50 -0400133GR_MAKE_BITFIELD_CLASS_OPS(FillRRectOp::ProcessorFlags)
Chris Dalton133944a2018-11-16 23:30:29 -0500134
135// Hardware derivatives are not always accurate enough for highly elliptical corners. This method
136// checks to make sure the corners will still all look good if we use HW derivatives.
Robert Phillips360ec182020-03-26 13:29:50 -0400137static bool can_use_hw_derivatives_with_coverage(const GrShaderCaps&,
138 const SkMatrix&,
139 const SkRRect&);
Chris Dalton133944a2018-11-16 23:30:29 -0500140
Herb Derbyc76d4092020-10-07 16:46:15 -0400141GrOp::Owner FillRRectOp::Make(GrRecordingContext* ctx,
142 GrPaint&& paint,
143 const SkMatrix& viewMatrix,
144 const SkRRect& rrect,
Chris Dalton4f447342021-03-12 12:09:12 -0700145 GrAA aa) {
Robert Phillips360ec182020-03-26 13:29:50 -0400146 using Helper = GrSimpleMeshDrawOpHelper;
147
148 const GrCaps* caps = ctx->priv().caps();
149
Chris Daltona77cdee2020-04-03 14:50:43 -0600150 if (!caps->drawInstancedSupport()) {
Chris Dalton133944a2018-11-16 23:30:29 -0500151 return nullptr;
152 }
153
Robert Phillips360ec182020-03-26 13:29:50 -0400154 ProcessorFlags flags = ProcessorFlags::kNone;
Chris Daltoncc13b352021-03-05 14:59:01 -0700155 // TODO: Support perspective in a follow-on CL. This shouldn't be difficult, since we already
156 // use HW derivatives. The only trick will be adjusting the AA outset to account for
157 // perspective. (i.e., outset = 0.5 * z.)
158 if (viewMatrix.hasPerspective()) {
159 return nullptr;
160 }
161 if (can_use_hw_derivatives_with_coverage(*caps->shaderCaps(), viewMatrix, rrect)) {
162 // HW derivatives (more specifically, fwidth()) are consistently faster on all platforms in
163 // coverage mode. We use them as long as the approximation will be accurate enough.
164 flags |= ProcessorFlags::kUseHWDerivatives;
165 }
Chris Dalton4f447342021-03-12 12:09:12 -0700166 if (aa == GrAA::kNo) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700167 flags |= ProcessorFlags::kFakeNonAA;
Chris Dalton133944a2018-11-16 23:30:29 -0500168 }
169
170 // Produce a matrix that draws the round rect from normalized [-1, -1, +1, +1] space.
171 float l = rrect.rect().left(), r = rrect.rect().right(),
172 t = rrect.rect().top(), b = rrect.rect().bottom();
173 SkMatrix m;
174 // Unmap the normalized rect [-1, -1, +1, +1] back to [l, t, r, b].
175 m.setScaleTranslate((r - l)/2, (b - t)/2, (l + r)/2, (t + b)/2);
176 // Map to device space.
177 m.postConcat(viewMatrix);
178
Chris Dalton0dffbab2019-03-27 13:08:50 -0600179 SkRect devBounds;
Chris Daltoncc13b352021-03-05 14:59:01 -0700180 // Since m is an affine matrix that maps the rect [-1, -1, +1, +1] into the shape's
181 // device-space quad, it's quite simple to find the bounding rectangle:
182 devBounds = SkRect::MakeXYWH(m.getTranslateX(), m.getTranslateY(), 0, 0);
183 devBounds.outset(SkScalarAbs(m.getScaleX()) + SkScalarAbs(m.getSkewX()),
184 SkScalarAbs(m.getSkewY()) + SkScalarAbs(m.getScaleY()));
Chris Dalton0dffbab2019-03-27 13:08:50 -0600185
Chris Daltoncc13b352021-03-05 14:59:01 -0700186 return Helper::FactoryHelper<FillRRectOp>(ctx, std::move(paint), m, rrect, flags, devBounds);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600187}
188
Herb Derbyc76d4092020-10-07 16:46:15 -0400189FillRRectOp::FillRRectOp(GrProcessorSet* processorSet,
Robert Phillips360ec182020-03-26 13:29:50 -0400190 const SkPMColor4f& paintColor,
191 const SkMatrix& totalShapeMatrix,
192 const SkRRect& rrect,
Robert Phillips360ec182020-03-26 13:29:50 -0400193 ProcessorFlags processorFlags,
Robert Phillips366176b2020-02-26 11:40:50 -0500194 const SkRect& devBounds)
Robert Phillipscad8fba2020-03-20 15:39:29 -0400195 : INHERITED(ClassID())
Chris Daltoncc13b352021-03-05 14:59:01 -0700196 , fHelper(processorSet,
197 (processorFlags & ProcessorFlags::kFakeNonAA)
198 ? GrAAType::kNone
199 : GrAAType::kCoverage) // Use analytic AA even if the RT is MSAA.
Robert Phillips360ec182020-03-26 13:29:50 -0400200 , fColor(paintColor)
Chris Dalton0dffbab2019-03-27 13:08:50 -0600201 , fLocalRect(rrect.rect())
Robert Phillips360ec182020-03-26 13:29:50 -0400202 , fProcessorFlags(processorFlags & ~(ProcessorFlags::kHasLocalCoords |
Chris Daltoncc13b352021-03-05 14:59:01 -0700203 ProcessorFlags::kWideColor |
204 ProcessorFlags::kMSAAEnabled)) {
205 // FillRRectOp::Make fails if there is perspective.
206 SkASSERT(!totalShapeMatrix.hasPerspective());
Chris Dalton4f447342021-03-12 12:09:12 -0700207 this->setBounds(devBounds, GrOp::HasAABloat(!(processorFlags & ProcessorFlags::kFakeNonAA)),
208 GrOp::IsHairline::kNo);
Chris Dalton133944a2018-11-16 23:30:29 -0500209
210 // Write the matrix attribs.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600211 const SkMatrix& m = totalShapeMatrix;
Chris Daltoncc13b352021-03-05 14:59:01 -0700212 // Affine 2D transformation (float2x2 plus float2 translate).
213 SkASSERT(!m.hasPerspective());
214 this->writeInstanceData(m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY());
215 this->writeInstanceData(m.getTranslateX(), m.getTranslateY());
Chris Dalton133944a2018-11-16 23:30:29 -0500216
217 // Convert the radii to [-1, -1, +1, +1] space and write their attribs.
218 Sk4f radiiX, radiiY;
219 Sk4f::Load2(SkRRectPriv::GetRadiiArray(rrect), &radiiX, &radiiY);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600220 (radiiX * (2/rrect.width())).store(this->appendInstanceData<float>(4));
221 (radiiY * (2/rrect.height())).store(this->appendInstanceData<float>(4));
Chris Dalton133944a2018-11-16 23:30:29 -0500222
223 // We will write the color and local rect attribs during finalize().
224}
225
Robert Phillips366176b2020-02-26 11:40:50 -0500226GrProcessorSet::Analysis FillRRectOp::finalize(
Chris Dalton6ce447a2019-06-23 18:07:38 -0600227 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
228 GrClampType clampType) {
Chris Dalton133944a2018-11-16 23:30:29 -0500229 SkASSERT(1 == fInstanceCount);
230
Robert Phillips360ec182020-03-26 13:29:50 -0400231 bool isWideColor;
232 auto analysis = fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
233 GrProcessorAnalysisCoverage::kSingleChannel,
234 &fColor, &isWideColor);
Chris Dalton133944a2018-11-16 23:30:29 -0500235
236 // Finish writing the instance attribs.
Robert Phillips360ec182020-03-26 13:29:50 -0400237 if (isWideColor) {
238 fProcessorFlags |= ProcessorFlags::kWideColor;
239 this->writeInstanceData(fColor);
Brian Osman5105d682019-02-13 16:06:14 -0500240 } else {
Robert Phillips360ec182020-03-26 13:29:50 -0400241 this->writeInstanceData(fColor.toBytes_RGBA());
Brian Osman5105d682019-02-13 16:06:14 -0500242 }
243
Chris Dalton133944a2018-11-16 23:30:29 -0500244 if (analysis.usesLocalCoords()) {
Robert Phillips360ec182020-03-26 13:29:50 -0400245 fProcessorFlags |= ProcessorFlags::kHasLocalCoords;
Chris Dalton133944a2018-11-16 23:30:29 -0500246 this->writeInstanceData(fLocalRect);
Chris Dalton133944a2018-11-16 23:30:29 -0500247 }
248 fInstanceStride = fInstanceData.count();
249
Chris Dalton4b62aed2019-01-15 11:53:00 -0700250 return analysis;
Chris Dalton133944a2018-11-16 23:30:29 -0500251}
252
Herb Derbye25c3002020-10-27 15:57:27 -0400253GrOp::CombineResult FillRRectOp::onCombineIfPossible(GrOp* op, SkArenaAlloc*, const GrCaps& caps) {
Robert Phillips366176b2020-02-26 11:40:50 -0500254 const auto& that = *op->cast<FillRRectOp>();
Robert Phillips360ec182020-03-26 13:29:50 -0400255 if (!fHelper.isCompatible(that.fHelper, caps, this->bounds(), that.bounds())) {
256 return CombineResult::kCannotCombine;
257 }
258
259 if (fProcessorFlags != that.fProcessorFlags ||
Chris Dalton133944a2018-11-16 23:30:29 -0500260 fInstanceData.count() > std::numeric_limits<int>::max() - that.fInstanceData.count()) {
261 return CombineResult::kCannotCombine;
262 }
263
264 fInstanceData.push_back_n(that.fInstanceData.count(), that.fInstanceData.begin());
265 fInstanceCount += that.fInstanceCount;
266 SkASSERT(fInstanceStride == that.fInstanceStride);
267 return CombineResult::kMerged;
268}
269
Robert Phillips366176b2020-02-26 11:40:50 -0500270class FillRRectOp::Processor : public GrGeometryProcessor {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600271public:
Robert Phillips360ec182020-03-26 13:29:50 -0400272 static GrGeometryProcessor* Make(SkArenaAlloc* arena, GrAAType aaType, ProcessorFlags flags) {
Mike Kleinf1241082020-12-14 15:59:09 -0600273 return arena->make([&](void* ptr) {
274 return new (ptr) Processor(aaType, flags);
275 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500276 }
277
Robert Phillips8053c972019-11-21 10:44:53 -0500278 const char* name() const final { return "GrFillRRectOp::Processor"; }
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500279
Robert Phillips8053c972019-11-21 10:44:53 -0500280 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const final {
Chris Daltoncc13b352021-03-05 14:59:01 -0700281 b->addBits(kNumProcessorFlags, (uint32_t)fFlags, "flags");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500282 }
283
Robert Phillips8053c972019-11-21 10:44:53 -0500284 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500285
286private:
Robert Phillips360ec182020-03-26 13:29:50 -0400287 Processor(GrAAType aaType, ProcessorFlags flags)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500288 : INHERITED(kGrFillRRectOp_Processor_ClassID)
Chris Dalton0dffbab2019-03-27 13:08:50 -0600289 , fFlags(flags) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700290 this->setVertexAttributes(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
Chris Dalton133944a2018-11-16 23:30:29 -0500291
Chris Daltoncc13b352021-03-05 14:59:01 -0700292 fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
293 fInstanceAttribs.emplace_back("translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600294 fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
295 fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
296 fColorAttrib = &fInstanceAttribs.push_back(
Robert Phillips360ec182020-03-26 13:29:50 -0400297 MakeColorAttribute("color", (fFlags & ProcessorFlags::kWideColor)));
298 if (fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600299 fInstanceAttribs.emplace_back(
300 "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
301 }
302 this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
Chris Dalton0dffbab2019-03-27 13:08:50 -0600303 }
304
Chris Dalton0dffbab2019-03-27 13:08:50 -0600305 static constexpr Attribute kVertexAttribs[] = {
306 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
307 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
308 // Coverage only.
309 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
310
Robert Phillips360ec182020-03-26 13:29:50 -0400311 const ProcessorFlags fFlags;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600312
313 SkSTArray<6, Attribute> fInstanceAttribs;
314 const Attribute* fColorAttrib;
315
Chris Daltoncc13b352021-03-05 14:59:01 -0700316 class Impl;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500317
John Stiles7571f9e2020-09-02 22:42:33 -0400318 using INHERITED = GrGeometryProcessor;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600319};
320
Robert Phillips366176b2020-02-26 11:40:50 -0500321constexpr GrPrimitiveProcessor::Attribute FillRRectOp::Processor::kVertexAttribs[];
Chris Dalton0dffbab2019-03-27 13:08:50 -0600322
323// Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
Chris Dalton133944a2018-11-16 23:30:29 -0500324// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
325// edges. The Vertex struct tells the shader where to place its vertex within a normalized
326// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600327struct CoverageVertex {
Chris Dalton133944a2018-11-16 23:30:29 -0500328 std::array<float, 4> fRadiiSelector;
329 std::array<float, 2> fCorner;
330 std::array<float, 2> fRadiusOutset;
331 std::array<float, 2> fAABloatDirection;
332 float fCoverage;
333 float fIsLinearCoverage;
Chris Dalton133944a2018-11-16 23:30:29 -0500334};
335
336// This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
337// of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
338// rectangles.
339static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
340
Chris Daltoncc13b352021-03-05 14:59:01 -0700341static constexpr CoverageVertex kVertexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500342 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700343 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
344 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500345
346 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700347 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
348 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500349
350 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700351 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
352 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500353
354 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700355 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
356 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500357
358
359 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700360 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
361 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500362
363 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700364 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
365 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500366
367 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700368 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
369 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500370
371 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700372 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
373 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500374
375
376 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700377 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
378 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
379 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
380 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
381 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
382 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500383
384 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700385 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
386 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
387 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
388 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
389 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
390 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500391
392 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700393 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
394 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
395 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
396 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
397 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
398 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500399
400 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700401 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
402 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
403 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
404 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
Chris Dalton2d07e862018-11-26 12:30:47 -0700405 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700406 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500407
Chris Daltoncc13b352021-03-05 14:59:01 -0700408GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500409
Chris Daltoncc13b352021-03-05 14:59:01 -0700410static constexpr uint16_t kIndexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500411 // Inset octagon (solid coverage).
412 0, 1, 7,
413 1, 2, 7,
414 7, 2, 6,
415 2, 3, 6,
416 6, 3, 5,
417 3, 4, 5,
418
419 // AA borders (linear coverage).
420 0, 1, 8, 1, 9, 8,
421 2, 3, 10, 3, 11, 10,
422 4, 5, 12, 5, 13, 12,
423 6, 7, 14, 7, 15, 14,
424
425 // Top-left arc.
426 16, 17, 21,
427 17, 21, 18,
428 21, 18, 20,
429 18, 20, 19,
430
431 // Top-right arc.
432 22, 23, 27,
433 23, 27, 24,
434 27, 24, 26,
435 24, 26, 25,
436
437 // Bottom-right arc.
438 28, 29, 33,
439 29, 33, 30,
440 33, 30, 32,
441 30, 32, 31,
442
443 // Bottom-left arc.
444 34, 35, 39,
445 35, 39, 36,
446 39, 36, 38,
447 36, 38, 37};
448
Chris Daltoncc13b352021-03-05 14:59:01 -0700449GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400450
Robert Phillipscad8fba2020-03-20 15:39:29 -0400451void FillRRectOp::onPrepareDraws(Target* target) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700452 // We request no multisample, but some platforms don't support disabling it on MSAA targets.
453 if (target->rtProxy()->numSamples() > 1 && !target->caps().multisampleDisableSupport()) {
454 fProcessorFlags |= ProcessorFlags::kMSAAEnabled;
455 }
456
Robert Phillipscad8fba2020-03-20 15:39:29 -0400457 if (void* instanceData = target->makeVertexSpace(fInstanceStride, fInstanceCount,
458 &fInstanceBuffer, &fBaseInstance)) {
Greg Danielf793de12019-09-05 13:23:23 -0400459 SkASSERT(fInstanceStride * fInstanceCount == fInstanceData.count());
460 memcpy(instanceData, fInstanceData.begin(), fInstanceData.count());
461 }
462
Chris Daltoncc13b352021-03-05 14:59:01 -0700463 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400464
Chris Daltoncc13b352021-03-05 14:59:01 -0700465 fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kIndex,
466 sizeof(kIndexData),
467 kIndexData, gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400468
Chris Daltoncc13b352021-03-05 14:59:01 -0700469 GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400470
Chris Daltoncc13b352021-03-05 14:59:01 -0700471 fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,
472 sizeof(kVertexData),
473 kVertexData,
474 gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400475}
476
Chris Daltoncc13b352021-03-05 14:59:01 -0700477class FillRRectOp::Processor::Impl : public GrGLSLGeometryProcessor {
Chris Dalton133944a2018-11-16 23:30:29 -0500478 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
John Stiles4d7ac492021-03-09 20:16:43 -0500479 GrGLSLVertexBuilder* v = args.fVertBuilder;
480 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
481
Chris Dalton133944a2018-11-16 23:30:29 -0500482 const auto& proc = args.fGP.cast<Processor>();
Robert Phillips360ec182020-03-26 13:29:50 -0400483 bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
Chris Dalton133944a2018-11-16 23:30:29 -0500484
Chris Dalton0dffbab2019-03-27 13:08:50 -0600485 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
486
Chris Dalton133944a2018-11-16 23:30:29 -0500487 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
488 varyings->emitAttributes(proc);
John Stiles4d7ac492021-03-09 20:16:43 -0500489 f->codeAppendf("half4 %s;", args.fOutputColor);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600490 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500491 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
492
493 // Emit the vertex shader.
Chris Daltoncc13b352021-03-05 14:59:01 -0700494 // When MSAA is enabled, we need to make sure every sample gets lit up on pixels that have
495 // fractional coverage. We do this by making the ramp wider.
496 v->codeAppendf("float aa_bloat_multiplier = %i;",
497 (proc.fFlags & ProcessorFlags::kMSAAEnabled)
498 ? 2 // Outset an entire pixel (2 radii).
499 : (!(proc.fFlags & ProcessorFlags::kFakeNonAA))
500 ? 1 // Outset one half pixel (1 radius).
501 : 0); // No AA bloat.
502
Chris Dalton133944a2018-11-16 23:30:29 -0500503 // Unpack vertex attribs.
504 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
505 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
506 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
Chris Dalton133944a2018-11-16 23:30:29 -0500507 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
508
509 // Find the amount to bloat each edge for AA (in source space).
510 v->codeAppend("float2 pixellength = inversesqrt("
511 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
512 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
513 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
514 "abs(normalized_axis_dirs.zw));");
515 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
516
517 // Identify our radii.
Mike Reedd3efa992018-11-28 13:13:15 +0000518 v->codeAppend("float4 radii_and_neighbors = radii_selector"
519 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
520 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
521 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500522
Chris Daltoncc13b352021-03-05 14:59:01 -0700523 v->codeAppend("float coverage_multiplier = 1;");
Chris Dalton133944a2018-11-16 23:30:29 -0500524 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700525 // The rrect is more narrow than a half-pixel AA coverage ramp. We can't
526 // draw as-is or else opposite AA borders will overlap. Instead, fudge the
527 // size up to the width of a coverage ramp, and then reduce total coverage
528 // to make the rect appear more thin.
Chris Dalton133944a2018-11-16 23:30:29 -0500529 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
Chris Daltoncc13b352021-03-05 14:59:01 -0700530 v->codeAppend( "coverage_multiplier = 1 / (max(aa_bloatradius.x, 1) * "
531 "max(aa_bloatradius.y, 1));");
Chris Dalton133944a2018-11-16 23:30:29 -0500532 // Set radii to zero to ensure we take the "linear coverage" codepath.
533 // (The "coverage" variable only has effect in the linear codepath.)
534 v->codeAppend( "radii = float2(0);");
535 v->codeAppend("}");
536
Chris Daltoncc13b352021-03-05 14:59:01 -0700537 // Unpack coverage.
538 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
539 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
540 // MSAA has a wider ramp that goes from -.5 to 1.5 instead of 0 to 1.
541 v->codeAppendf("coverage = (coverage - .5) * aa_bloat_multiplier + .5;");
542 }
543
544 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.5))) {");
Chris Dalton133944a2018-11-16 23:30:29 -0500545 // The radii are very small. Demote this arc to a sharp 90 degree corner.
Chris Daltoncc13b352021-03-05 14:59:01 -0700546 v->codeAppend( "radii = float2(0);");
547 // Convert to a standard picture frame for an AA rect instead of the round
548 // rect geometry.
549 v->codeAppend( "aa_bloat_direction = sign(corner);");
550 v->codeAppend( "if (coverage > .5) {"); // Are we an inset edge?
551 v->codeAppend( "aa_bloat_direction = -aa_bloat_direction;");
552 v->codeAppend( "}");
Chris Dalton133944a2018-11-16 23:30:29 -0500553 v->codeAppend( "is_linear_coverage = 1;");
554 v->codeAppend("} else {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700555 // Don't let radii get smaller than a coverage ramp plus an extra half
556 // pixel for MSAA. Always use the same amount so we don't pop when
557 // switching between MSAA and coverage.
558 v->codeAppend( "radii = clamp(radii, pixellength * 1.5, 2 - pixellength * 1.5);");
559 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength * 1.5, "
560 "2 - pixellength * 1.5);");
Mike Reedd3efa992018-11-28 13:13:15 +0000561 // Don't let neighboring radii get closer together than 1/16 pixel.
562 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
563 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
564 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500565 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500566
567 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
568 // normalized [-1,-1,+1,+1] space.
Chris Daltoncc13b352021-03-05 14:59:01 -0700569 v->codeAppend("float2 aa_outset = "
570 "aa_bloat_direction * aa_bloatradius * aa_bloat_multiplier;");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700571 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500572
Chris Daltoncc13b352021-03-05 14:59:01 -0700573 v->codeAppend("if (coverage > .5) {"); // Are we an inset edge?
574 // Don't allow the aa insets to overlap. i.e., Don't let them inset past
575 // the center (x=y=0). Since we don't allow the rect to become thinner
576 // than 1px, this should only happen when using MSAA, where we inset by an
577 // entire pixel instead of half.
578 v->codeAppend( "if (aa_bloat_direction.x != 0 && vertexpos.x * corner.x < 0) {");
579 v->codeAppend( "float backset = abs(vertexpos.x);");
580 v->codeAppend( "vertexpos.x = 0;");
581 v->codeAppend( "vertexpos.y += "
582 "backset * sign(corner.y) * pixellength.y/pixellength.x;");
583 v->codeAppend( "coverage = (coverage - .5) * abs(corner.x) / "
584 "(abs(corner.x) + backset) + .5;");
585 v->codeAppend( "}");
586 v->codeAppend( "if (aa_bloat_direction.y != 0 && vertexpos.y * corner.y < 0) {");
587 v->codeAppend( "float backset = abs(vertexpos.y);");
588 v->codeAppend( "vertexpos.y = 0;");
589 v->codeAppend( "vertexpos.x += "
590 "backset * sign(corner.x) * pixellength.x/pixellength.y;");
591 v->codeAppend( "coverage = (coverage - .5) * abs(corner.y) / "
592 "(abs(corner.y) + backset) + .5;");
593 v->codeAppend( "}");
594 v->codeAppend("}");
595
Michael Ludwig553db622020-06-19 10:47:30 -0400596 // Write positions
Chris Dalton133944a2018-11-16 23:30:29 -0500597 GrShaderVar localCoord("", kFloat2_GrSLType);
Robert Phillips360ec182020-03-26 13:29:50 -0400598 if (proc.fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton133944a2018-11-16 23:30:29 -0500599 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
600 "local_rect.zw * (1 + vertexpos)) * .5;");
Michael Ludwig553db622020-06-19 10:47:30 -0400601 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
Chris Dalton133944a2018-11-16 23:30:29 -0500602 }
Chris Dalton133944a2018-11-16 23:30:29 -0500603
604 // Transform to device space.
605 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
606 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
607 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
608
609 // Setup interpolants for coverage.
610 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
611 varyings->addVarying("arccoord", &arcCoord);
612 v->codeAppend("if (0 != is_linear_coverage) {");
613 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
614 // interpolate linear coverage across y.
Chris Daltoncc13b352021-03-05 14:59:01 -0700615 v->codeAppendf( "%s.xy = float2(0, coverage * coverage_multiplier);",
616 arcCoord.vsOut());
Chris Dalton133944a2018-11-16 23:30:29 -0500617 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700618 // Find the normalized arc coordinates for our corner ellipse.
619 // (i.e., the coordinate system where x^2 + y^2 == 1).
620 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500621 // We are a corner piece: Interpolate the arc coordinates for coverage.
622 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
623 // instructs the fragment shader to use linear coverage).
624 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
625 if (!useHWDerivatives) {
626 // The gradient is order-1: Interpolate it across arccoord.zw.
627 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
628 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
629 }
630 v->codeAppend("}");
631
632 // Emit the fragment shader.
Chris Dalton133944a2018-11-16 23:30:29 -0500633 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
634 f->codeAppendf("half coverage;");
635 f->codeAppendf("if (0 == x_plus_1) {");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600636 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500637 f->codeAppendf("} else {");
638 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
639 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
640 if (useHWDerivatives) {
641 f->codeAppendf("float fnwidth = fwidth(fn);");
642 } else {
643 // The gradient is interpolated across arccoord.zw.
644 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
645 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
646 }
Chris Daltoncc13b352021-03-05 14:59:01 -0700647 f->codeAppendf( "coverage = .5 - half(fn/fnwidth);");
648 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
649 // MSAA uses ramps larger than 1px, so we need to clamp in both branches.
650 f->codeAppendf("}");
651 }
652 f->codeAppendf("coverage = clamp(coverage, 0, 1);");
653 if (!(proc.fFlags & ProcessorFlags::kMSAAEnabled)) {
654 // When not using MSAA, we only need to clamp in the "arc" branch.
655 f->codeAppendf("}");
656 }
657 if (proc.fFlags & ProcessorFlags::kFakeNonAA) {
658 f->codeAppendf("coverage = (coverage >= .5) ? 1 : 0;");
659 }
John Stiles4d7ac492021-03-09 20:16:43 -0500660 f->codeAppendf("half4 %s = half4(coverage);", args.fOutputCoverage);
Chris Dalton133944a2018-11-16 23:30:29 -0500661 }
662
Brian Osman609f1592020-07-01 15:14:39 -0400663 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&) override {}
Chris Dalton133944a2018-11-16 23:30:29 -0500664};
665
Chris Dalton0dffbab2019-03-27 13:08:50 -0600666
Robert Phillips366176b2020-02-26 11:40:50 -0500667GrGLSLPrimitiveProcessor* FillRRectOp::Processor::createGLSLInstance(
Chris Dalton133944a2018-11-16 23:30:29 -0500668 const GrShaderCaps&) const {
Chris Daltoncc13b352021-03-05 14:59:01 -0700669 return new Impl();
Chris Dalton133944a2018-11-16 23:30:29 -0500670}
671
Robert Phillipscad8fba2020-03-20 15:39:29 -0400672void FillRRectOp::onCreateProgramInfo(const GrCaps* caps,
673 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -0500674 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400675 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -0400676 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500677 GrXferBarrierFlags renderPassXferBarriers,
678 GrLoadOp colorLoadOp) {
Robert Phillips360ec182020-03-26 13:29:50 -0400679 GrGeometryProcessor* gp = Processor::Make(arena, fHelper.aaType(), fProcessorFlags);
Robert Phillipsce978572020-02-28 11:56:44 -0500680 SkASSERT(gp->instanceStride() == (size_t)fInstanceStride);
Chris Dalton133944a2018-11-16 23:30:29 -0500681
Brian Salomon8afde5f2020-04-01 16:22:00 -0400682 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -0400683 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -0500684 renderPassXferBarriers, colorLoadOp);
Robert Phillips8053c972019-11-21 10:44:53 -0500685}
686
Robert Phillips366176b2020-02-26 11:40:50 -0500687void FillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Robert Phillips8053c972019-11-21 10:44:53 -0500688 if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
689 return; // Setup failed.
690 }
691
692 if (!fProgramInfo) {
Robert Phillipscad8fba2020-03-20 15:39:29 -0400693 this->createProgramInfo(flushState);
Robert Phillips8053c972019-11-21 10:44:53 -0500694 }
Robert Phillips901aff02019-10-08 12:32:56 -0400695
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600696 flushState->bindPipelineAndScissorClip(*fProgramInfo, this->bounds());
697 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
Greg Daniel426274b2020-07-20 11:37:38 -0400698 flushState->bindBuffers(std::move(fIndexBuffer), std::move(fInstanceBuffer),
699 std::move(fVertexBuffer));
Chris Daltoncc13b352021-03-05 14:59:01 -0700700 flushState->drawIndexedInstanced(SK_ARRAY_COUNT(kIndexData), 0, fInstanceCount, fBaseInstance,
701 0);
Chris Dalton133944a2018-11-16 23:30:29 -0500702}
703
704// Will the given corner look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600705static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
Chris Dalton133944a2018-11-16 23:30:29 -0500706 Sk2f devRadii = devScale * cornerRadii;
707 if (devRadii[1] < devRadii[0]) {
708 devRadii = SkNx_shuffle<1,0>(devRadii);
709 }
Brian Osman788b9162020-02-07 10:36:46 -0500710 float minDevRadius = std::max(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
Chris Dalton133944a2018-11-16 23:30:29 -0500711 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
712 // This threshold was arrived at subjevtively on an NVIDIA chip.
713 return minDevRadius * minDevRadius * 5 > devRadii[1];
714}
715
Chris Dalton0dffbab2019-03-27 13:08:50 -0600716static bool can_use_hw_derivatives_with_coverage(
717 const Sk2f& devScale, const SkVector& cornerRadii) {
718 return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
Chris Dalton133944a2018-11-16 23:30:29 -0500719}
720
721// Will the given round rect look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600722static bool can_use_hw_derivatives_with_coverage(
723 const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
Chris Dalton133944a2018-11-16 23:30:29 -0500724 if (!shaderCaps.shaderDerivativeSupport()) {
725 return false;
726 }
727
728 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
729 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
730 Sk2f devScale = (x*x + y*y).sqrt();
731 switch (rrect.getType()) {
732 case SkRRect::kEmpty_Type:
733 case SkRRect::kRect_Type:
734 return true;
735
736 case SkRRect::kOval_Type:
737 case SkRRect::kSimple_Type:
Chris Dalton0dffbab2019-03-27 13:08:50 -0600738 return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
Chris Dalton133944a2018-11-16 23:30:29 -0500739
740 case SkRRect::kNinePatch_Type: {
741 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
742 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
743 Sk2f minRadii = Sk2f::Min(r0, r1);
744 Sk2f maxRadii = Sk2f::Max(r0, r1);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600745 return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
746 can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
Chris Dalton133944a2018-11-16 23:30:29 -0500747 }
748
749 case SkRRect::kComplex_Type: {
750 for (int i = 0; i < 4; ++i) {
751 auto corner = static_cast<SkRRect::Corner>(i);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600752 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
Chris Dalton133944a2018-11-16 23:30:29 -0500753 return false;
754 }
755 }
756 return true;
757 }
758 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600759 SK_ABORT("Invalid round rect type.");
Chris Dalton133944a2018-11-16 23:30:29 -0500760}
Robert Phillips366176b2020-02-26 11:40:50 -0500761
762} // anonymous namespace
763
764
Herb Derbyc76d4092020-10-07 16:46:15 -0400765GrOp::Owner GrFillRRectOp::Make(GrRecordingContext* ctx,
766 GrPaint&& paint,
767 const SkMatrix& viewMatrix,
768 const SkRRect& rrect,
Chris Dalton4f447342021-03-12 12:09:12 -0700769 GrAA aa) {
770 return FillRRectOp::Make(ctx, std::move(paint), viewMatrix, rrect, aa);
Robert Phillips366176b2020-02-26 11:40:50 -0500771}
772
773#if GR_TEST_UTILS
774
775#include "src/gpu/GrDrawOpTest.h"
776
777GR_DRAW_OP_TEST_DEFINE(FillRRectOp) {
Robert Phillips366176b2020-02-26 11:40:50 -0500778 SkMatrix viewMatrix = GrTest::TestMatrix(random);
Chris Dalton4f447342021-03-12 12:09:12 -0700779 GrAA aa = GrAA(random->nextBool());
Robert Phillips366176b2020-02-26 11:40:50 -0500780
781 SkRect rect = GrTest::TestRect(random);
782 float w = rect.width();
783 float h = rect.height();
784
785 SkRRect rrect;
786 // TODO: test out other rrect configurations
787 rrect.setNinePatch(rect, w / 3.0f, h / 4.0f, w / 5.0f, h / 6.0);
788
789 return GrFillRRectOp::Make(context,
Robert Phillips360ec182020-03-26 13:29:50 -0400790 std::move(paint),
Robert Phillips366176b2020-02-26 11:40:50 -0500791 viewMatrix,
792 rrect,
Chris Dalton4f447342021-03-12 12:09:12 -0700793 aa);
Robert Phillips366176b2020-02-26 11:40:50 -0500794}
795
796#endif