blob: 59da7f19cd5108fd6fe8b45be14b1fc2ef7023a7 [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&,
38 GrAAType);
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,
145 GrAAType aaType) {
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 }
166 if (aaType == GrAAType::kNone) {
167 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());
Greg Daniel5faf4742019-10-01 15:14:44 -0400207 this->setBounds(devBounds, GrOp::HasAABloat::kYes, GrOp::IsHairline::kNo);
Chris Dalton133944a2018-11-16 23:30:29 -0500208
209 // Write the matrix attribs.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600210 const SkMatrix& m = totalShapeMatrix;
Chris Daltoncc13b352021-03-05 14:59:01 -0700211 // Affine 2D transformation (float2x2 plus float2 translate).
212 SkASSERT(!m.hasPerspective());
213 this->writeInstanceData(m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY());
214 this->writeInstanceData(m.getTranslateX(), m.getTranslateY());
Chris Dalton133944a2018-11-16 23:30:29 -0500215
216 // Convert the radii to [-1, -1, +1, +1] space and write their attribs.
217 Sk4f radiiX, radiiY;
218 Sk4f::Load2(SkRRectPriv::GetRadiiArray(rrect), &radiiX, &radiiY);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600219 (radiiX * (2/rrect.width())).store(this->appendInstanceData<float>(4));
220 (radiiY * (2/rrect.height())).store(this->appendInstanceData<float>(4));
Chris Dalton133944a2018-11-16 23:30:29 -0500221
222 // We will write the color and local rect attribs during finalize().
223}
224
Robert Phillips366176b2020-02-26 11:40:50 -0500225GrProcessorSet::Analysis FillRRectOp::finalize(
Chris Dalton6ce447a2019-06-23 18:07:38 -0600226 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
227 GrClampType clampType) {
Chris Dalton133944a2018-11-16 23:30:29 -0500228 SkASSERT(1 == fInstanceCount);
229
Robert Phillips360ec182020-03-26 13:29:50 -0400230 bool isWideColor;
231 auto analysis = fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
232 GrProcessorAnalysisCoverage::kSingleChannel,
233 &fColor, &isWideColor);
Chris Dalton133944a2018-11-16 23:30:29 -0500234
235 // Finish writing the instance attribs.
Robert Phillips360ec182020-03-26 13:29:50 -0400236 if (isWideColor) {
237 fProcessorFlags |= ProcessorFlags::kWideColor;
238 this->writeInstanceData(fColor);
Brian Osman5105d682019-02-13 16:06:14 -0500239 } else {
Robert Phillips360ec182020-03-26 13:29:50 -0400240 this->writeInstanceData(fColor.toBytes_RGBA());
Brian Osman5105d682019-02-13 16:06:14 -0500241 }
242
Chris Dalton133944a2018-11-16 23:30:29 -0500243 if (analysis.usesLocalCoords()) {
Robert Phillips360ec182020-03-26 13:29:50 -0400244 fProcessorFlags |= ProcessorFlags::kHasLocalCoords;
Chris Dalton133944a2018-11-16 23:30:29 -0500245 this->writeInstanceData(fLocalRect);
Chris Dalton133944a2018-11-16 23:30:29 -0500246 }
247 fInstanceStride = fInstanceData.count();
248
Chris Dalton4b62aed2019-01-15 11:53:00 -0700249 return analysis;
Chris Dalton133944a2018-11-16 23:30:29 -0500250}
251
Herb Derbye25c3002020-10-27 15:57:27 -0400252GrOp::CombineResult FillRRectOp::onCombineIfPossible(GrOp* op, SkArenaAlloc*, const GrCaps& caps) {
Robert Phillips366176b2020-02-26 11:40:50 -0500253 const auto& that = *op->cast<FillRRectOp>();
Robert Phillips360ec182020-03-26 13:29:50 -0400254 if (!fHelper.isCompatible(that.fHelper, caps, this->bounds(), that.bounds())) {
255 return CombineResult::kCannotCombine;
256 }
257
258 if (fProcessorFlags != that.fProcessorFlags ||
Chris Dalton133944a2018-11-16 23:30:29 -0500259 fInstanceData.count() > std::numeric_limits<int>::max() - that.fInstanceData.count()) {
260 return CombineResult::kCannotCombine;
261 }
262
263 fInstanceData.push_back_n(that.fInstanceData.count(), that.fInstanceData.begin());
264 fInstanceCount += that.fInstanceCount;
265 SkASSERT(fInstanceStride == that.fInstanceStride);
266 return CombineResult::kMerged;
267}
268
Robert Phillips366176b2020-02-26 11:40:50 -0500269class FillRRectOp::Processor : public GrGeometryProcessor {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600270public:
Robert Phillips360ec182020-03-26 13:29:50 -0400271 static GrGeometryProcessor* Make(SkArenaAlloc* arena, GrAAType aaType, ProcessorFlags flags) {
Mike Kleinf1241082020-12-14 15:59:09 -0600272 return arena->make([&](void* ptr) {
273 return new (ptr) Processor(aaType, flags);
274 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500275 }
276
Robert Phillips8053c972019-11-21 10:44:53 -0500277 const char* name() const final { return "GrFillRRectOp::Processor"; }
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500278
Robert Phillips8053c972019-11-21 10:44:53 -0500279 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const final {
Chris Daltoncc13b352021-03-05 14:59:01 -0700280 b->addBits(kNumProcessorFlags, (uint32_t)fFlags, "flags");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500281 }
282
Robert Phillips8053c972019-11-21 10:44:53 -0500283 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500284
285private:
Robert Phillips360ec182020-03-26 13:29:50 -0400286 Processor(GrAAType aaType, ProcessorFlags flags)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500287 : INHERITED(kGrFillRRectOp_Processor_ClassID)
Chris Dalton0dffbab2019-03-27 13:08:50 -0600288 , fFlags(flags) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700289 this->setVertexAttributes(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
Chris Dalton133944a2018-11-16 23:30:29 -0500290
Chris Daltoncc13b352021-03-05 14:59:01 -0700291 fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
292 fInstanceAttribs.emplace_back("translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600293 fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
294 fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
295 fColorAttrib = &fInstanceAttribs.push_back(
Robert Phillips360ec182020-03-26 13:29:50 -0400296 MakeColorAttribute("color", (fFlags & ProcessorFlags::kWideColor)));
297 if (fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600298 fInstanceAttribs.emplace_back(
299 "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
300 }
301 this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
Chris Dalton0dffbab2019-03-27 13:08:50 -0600302 }
303
Chris Dalton0dffbab2019-03-27 13:08:50 -0600304 static constexpr Attribute kVertexAttribs[] = {
305 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
306 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
307 // Coverage only.
308 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
309
Robert Phillips360ec182020-03-26 13:29:50 -0400310 const ProcessorFlags fFlags;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600311
312 SkSTArray<6, Attribute> fInstanceAttribs;
313 const Attribute* fColorAttrib;
314
Chris Daltoncc13b352021-03-05 14:59:01 -0700315 class Impl;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500316
John Stiles7571f9e2020-09-02 22:42:33 -0400317 using INHERITED = GrGeometryProcessor;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600318};
319
Robert Phillips366176b2020-02-26 11:40:50 -0500320constexpr GrPrimitiveProcessor::Attribute FillRRectOp::Processor::kVertexAttribs[];
Chris Dalton0dffbab2019-03-27 13:08:50 -0600321
322// Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
Chris Dalton133944a2018-11-16 23:30:29 -0500323// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
324// edges. The Vertex struct tells the shader where to place its vertex within a normalized
325// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600326struct CoverageVertex {
Chris Dalton133944a2018-11-16 23:30:29 -0500327 std::array<float, 4> fRadiiSelector;
328 std::array<float, 2> fCorner;
329 std::array<float, 2> fRadiusOutset;
330 std::array<float, 2> fAABloatDirection;
331 float fCoverage;
332 float fIsLinearCoverage;
Chris Dalton133944a2018-11-16 23:30:29 -0500333};
334
335// This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
336// of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
337// rectangles.
338static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
339
Chris Daltoncc13b352021-03-05 14:59:01 -0700340static constexpr CoverageVertex kVertexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500341 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700342 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
343 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500344
345 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700346 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
347 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500348
349 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700350 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
351 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500352
353 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700354 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
355 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500356
357
358 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700359 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
360 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500361
362 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700363 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
364 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500365
366 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700367 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
368 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500369
370 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700371 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
372 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500373
374
375 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700376 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
377 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
378 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
379 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
380 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
381 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500382
383 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700384 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
385 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
386 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
387 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
388 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
389 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500390
391 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700392 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
393 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
394 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
395 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
396 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
397 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500398
399 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700400 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
401 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
402 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
403 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
Chris Dalton2d07e862018-11-26 12:30:47 -0700404 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700405 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500406
Chris Daltoncc13b352021-03-05 14:59:01 -0700407GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500408
Chris Daltoncc13b352021-03-05 14:59:01 -0700409static constexpr uint16_t kIndexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500410 // Inset octagon (solid coverage).
411 0, 1, 7,
412 1, 2, 7,
413 7, 2, 6,
414 2, 3, 6,
415 6, 3, 5,
416 3, 4, 5,
417
418 // AA borders (linear coverage).
419 0, 1, 8, 1, 9, 8,
420 2, 3, 10, 3, 11, 10,
421 4, 5, 12, 5, 13, 12,
422 6, 7, 14, 7, 15, 14,
423
424 // Top-left arc.
425 16, 17, 21,
426 17, 21, 18,
427 21, 18, 20,
428 18, 20, 19,
429
430 // Top-right arc.
431 22, 23, 27,
432 23, 27, 24,
433 27, 24, 26,
434 24, 26, 25,
435
436 // Bottom-right arc.
437 28, 29, 33,
438 29, 33, 30,
439 33, 30, 32,
440 30, 32, 31,
441
442 // Bottom-left arc.
443 34, 35, 39,
444 35, 39, 36,
445 39, 36, 38,
446 36, 38, 37};
447
Chris Daltoncc13b352021-03-05 14:59:01 -0700448GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400449
Robert Phillipscad8fba2020-03-20 15:39:29 -0400450void FillRRectOp::onPrepareDraws(Target* target) {
Chris Daltoncc13b352021-03-05 14:59:01 -0700451 // We request no multisample, but some platforms don't support disabling it on MSAA targets.
452 if (target->rtProxy()->numSamples() > 1 && !target->caps().multisampleDisableSupport()) {
453 fProcessorFlags |= ProcessorFlags::kMSAAEnabled;
454 }
455
Robert Phillipscad8fba2020-03-20 15:39:29 -0400456 if (void* instanceData = target->makeVertexSpace(fInstanceStride, fInstanceCount,
457 &fInstanceBuffer, &fBaseInstance)) {
Greg Danielf793de12019-09-05 13:23:23 -0400458 SkASSERT(fInstanceStride * fInstanceCount == fInstanceData.count());
459 memcpy(instanceData, fInstanceData.begin(), fInstanceData.count());
460 }
461
Chris Daltoncc13b352021-03-05 14:59:01 -0700462 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400463
Chris Daltoncc13b352021-03-05 14:59:01 -0700464 fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kIndex,
465 sizeof(kIndexData),
466 kIndexData, gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400467
Chris Daltoncc13b352021-03-05 14:59:01 -0700468 GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400469
Chris Daltoncc13b352021-03-05 14:59:01 -0700470 fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,
471 sizeof(kVertexData),
472 kVertexData,
473 gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400474}
475
Chris Daltoncc13b352021-03-05 14:59:01 -0700476class FillRRectOp::Processor::Impl : public GrGLSLGeometryProcessor {
Chris Dalton133944a2018-11-16 23:30:29 -0500477 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
John Stiles4d7ac492021-03-09 20:16:43 -0500478 GrGLSLVertexBuilder* v = args.fVertBuilder;
479 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
480
Chris Dalton133944a2018-11-16 23:30:29 -0500481 const auto& proc = args.fGP.cast<Processor>();
Robert Phillips360ec182020-03-26 13:29:50 -0400482 bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
Chris Dalton133944a2018-11-16 23:30:29 -0500483
Chris Dalton0dffbab2019-03-27 13:08:50 -0600484 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
485
Chris Dalton133944a2018-11-16 23:30:29 -0500486 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
487 varyings->emitAttributes(proc);
John Stiles4d7ac492021-03-09 20:16:43 -0500488 f->codeAppendf("half4 %s;", args.fOutputColor);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600489 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500490 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
491
492 // Emit the vertex shader.
Chris Daltoncc13b352021-03-05 14:59:01 -0700493 // When MSAA is enabled, we need to make sure every sample gets lit up on pixels that have
494 // fractional coverage. We do this by making the ramp wider.
495 v->codeAppendf("float aa_bloat_multiplier = %i;",
496 (proc.fFlags & ProcessorFlags::kMSAAEnabled)
497 ? 2 // Outset an entire pixel (2 radii).
498 : (!(proc.fFlags & ProcessorFlags::kFakeNonAA))
499 ? 1 // Outset one half pixel (1 radius).
500 : 0); // No AA bloat.
501
Chris Dalton133944a2018-11-16 23:30:29 -0500502 // Unpack vertex attribs.
503 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
504 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
505 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
Chris Dalton133944a2018-11-16 23:30:29 -0500506 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
507
508 // Find the amount to bloat each edge for AA (in source space).
509 v->codeAppend("float2 pixellength = inversesqrt("
510 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
511 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
512 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
513 "abs(normalized_axis_dirs.zw));");
514 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
515
516 // Identify our radii.
Mike Reedd3efa992018-11-28 13:13:15 +0000517 v->codeAppend("float4 radii_and_neighbors = radii_selector"
518 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
519 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
520 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500521
Chris Daltoncc13b352021-03-05 14:59:01 -0700522 v->codeAppend("float coverage_multiplier = 1;");
Chris Dalton133944a2018-11-16 23:30:29 -0500523 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700524 // The rrect is more narrow than a half-pixel AA coverage ramp. We can't
525 // draw as-is or else opposite AA borders will overlap. Instead, fudge the
526 // size up to the width of a coverage ramp, and then reduce total coverage
527 // to make the rect appear more thin.
Chris Dalton133944a2018-11-16 23:30:29 -0500528 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
Chris Daltoncc13b352021-03-05 14:59:01 -0700529 v->codeAppend( "coverage_multiplier = 1 / (max(aa_bloatradius.x, 1) * "
530 "max(aa_bloatradius.y, 1));");
Chris Dalton133944a2018-11-16 23:30:29 -0500531 // Set radii to zero to ensure we take the "linear coverage" codepath.
532 // (The "coverage" variable only has effect in the linear codepath.)
533 v->codeAppend( "radii = float2(0);");
534 v->codeAppend("}");
535
Chris Daltoncc13b352021-03-05 14:59:01 -0700536 // Unpack coverage.
537 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
538 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
539 // MSAA has a wider ramp that goes from -.5 to 1.5 instead of 0 to 1.
540 v->codeAppendf("coverage = (coverage - .5) * aa_bloat_multiplier + .5;");
541 }
542
543 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.5))) {");
Chris Dalton133944a2018-11-16 23:30:29 -0500544 // The radii are very small. Demote this arc to a sharp 90 degree corner.
Chris Daltoncc13b352021-03-05 14:59:01 -0700545 v->codeAppend( "radii = float2(0);");
546 // Convert to a standard picture frame for an AA rect instead of the round
547 // rect geometry.
548 v->codeAppend( "aa_bloat_direction = sign(corner);");
549 v->codeAppend( "if (coverage > .5) {"); // Are we an inset edge?
550 v->codeAppend( "aa_bloat_direction = -aa_bloat_direction;");
551 v->codeAppend( "}");
Chris Dalton133944a2018-11-16 23:30:29 -0500552 v->codeAppend( "is_linear_coverage = 1;");
553 v->codeAppend("} else {");
Chris Daltoncc13b352021-03-05 14:59:01 -0700554 // Don't let radii get smaller than a coverage ramp plus an extra half
555 // pixel for MSAA. Always use the same amount so we don't pop when
556 // switching between MSAA and coverage.
557 v->codeAppend( "radii = clamp(radii, pixellength * 1.5, 2 - pixellength * 1.5);");
558 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength * 1.5, "
559 "2 - pixellength * 1.5);");
Mike Reedd3efa992018-11-28 13:13:15 +0000560 // Don't let neighboring radii get closer together than 1/16 pixel.
561 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
562 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
563 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500564 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500565
566 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
567 // normalized [-1,-1,+1,+1] space.
Chris Daltoncc13b352021-03-05 14:59:01 -0700568 v->codeAppend("float2 aa_outset = "
569 "aa_bloat_direction * aa_bloatradius * aa_bloat_multiplier;");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700570 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500571
Chris Daltoncc13b352021-03-05 14:59:01 -0700572 v->codeAppend("if (coverage > .5) {"); // Are we an inset edge?
573 // Don't allow the aa insets to overlap. i.e., Don't let them inset past
574 // the center (x=y=0). Since we don't allow the rect to become thinner
575 // than 1px, this should only happen when using MSAA, where we inset by an
576 // entire pixel instead of half.
577 v->codeAppend( "if (aa_bloat_direction.x != 0 && vertexpos.x * corner.x < 0) {");
578 v->codeAppend( "float backset = abs(vertexpos.x);");
579 v->codeAppend( "vertexpos.x = 0;");
580 v->codeAppend( "vertexpos.y += "
581 "backset * sign(corner.y) * pixellength.y/pixellength.x;");
582 v->codeAppend( "coverage = (coverage - .5) * abs(corner.x) / "
583 "(abs(corner.x) + backset) + .5;");
584 v->codeAppend( "}");
585 v->codeAppend( "if (aa_bloat_direction.y != 0 && vertexpos.y * corner.y < 0) {");
586 v->codeAppend( "float backset = abs(vertexpos.y);");
587 v->codeAppend( "vertexpos.y = 0;");
588 v->codeAppend( "vertexpos.x += "
589 "backset * sign(corner.x) * pixellength.x/pixellength.y;");
590 v->codeAppend( "coverage = (coverage - .5) * abs(corner.y) / "
591 "(abs(corner.y) + backset) + .5;");
592 v->codeAppend( "}");
593 v->codeAppend("}");
594
Michael Ludwig553db622020-06-19 10:47:30 -0400595 // Write positions
Chris Dalton133944a2018-11-16 23:30:29 -0500596 GrShaderVar localCoord("", kFloat2_GrSLType);
Robert Phillips360ec182020-03-26 13:29:50 -0400597 if (proc.fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton133944a2018-11-16 23:30:29 -0500598 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
599 "local_rect.zw * (1 + vertexpos)) * .5;");
Michael Ludwig553db622020-06-19 10:47:30 -0400600 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
Chris Dalton133944a2018-11-16 23:30:29 -0500601 }
Chris Dalton133944a2018-11-16 23:30:29 -0500602
603 // Transform to device space.
604 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
605 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
606 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
607
608 // Setup interpolants for coverage.
609 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
610 varyings->addVarying("arccoord", &arcCoord);
611 v->codeAppend("if (0 != is_linear_coverage) {");
612 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
613 // interpolate linear coverage across y.
Chris Daltoncc13b352021-03-05 14:59:01 -0700614 v->codeAppendf( "%s.xy = float2(0, coverage * coverage_multiplier);",
615 arcCoord.vsOut());
Chris Dalton133944a2018-11-16 23:30:29 -0500616 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700617 // Find the normalized arc coordinates for our corner ellipse.
618 // (i.e., the coordinate system where x^2 + y^2 == 1).
619 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500620 // We are a corner piece: Interpolate the arc coordinates for coverage.
621 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
622 // instructs the fragment shader to use linear coverage).
623 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
624 if (!useHWDerivatives) {
625 // The gradient is order-1: Interpolate it across arccoord.zw.
626 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
627 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
628 }
629 v->codeAppend("}");
630
631 // Emit the fragment shader.
Chris Dalton133944a2018-11-16 23:30:29 -0500632 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
633 f->codeAppendf("half coverage;");
634 f->codeAppendf("if (0 == x_plus_1) {");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600635 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500636 f->codeAppendf("} else {");
637 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
638 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
639 if (useHWDerivatives) {
640 f->codeAppendf("float fnwidth = fwidth(fn);");
641 } else {
642 // The gradient is interpolated across arccoord.zw.
643 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
644 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
645 }
Chris Daltoncc13b352021-03-05 14:59:01 -0700646 f->codeAppendf( "coverage = .5 - half(fn/fnwidth);");
647 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
648 // MSAA uses ramps larger than 1px, so we need to clamp in both branches.
649 f->codeAppendf("}");
650 }
651 f->codeAppendf("coverage = clamp(coverage, 0, 1);");
652 if (!(proc.fFlags & ProcessorFlags::kMSAAEnabled)) {
653 // When not using MSAA, we only need to clamp in the "arc" branch.
654 f->codeAppendf("}");
655 }
656 if (proc.fFlags & ProcessorFlags::kFakeNonAA) {
657 f->codeAppendf("coverage = (coverage >= .5) ? 1 : 0;");
658 }
John Stiles4d7ac492021-03-09 20:16:43 -0500659 f->codeAppendf("half4 %s = half4(coverage);", args.fOutputCoverage);
Chris Dalton133944a2018-11-16 23:30:29 -0500660 }
661
Brian Osman609f1592020-07-01 15:14:39 -0400662 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&) override {}
Chris Dalton133944a2018-11-16 23:30:29 -0500663};
664
Chris Dalton0dffbab2019-03-27 13:08:50 -0600665
Robert Phillips366176b2020-02-26 11:40:50 -0500666GrGLSLPrimitiveProcessor* FillRRectOp::Processor::createGLSLInstance(
Chris Dalton133944a2018-11-16 23:30:29 -0500667 const GrShaderCaps&) const {
Chris Daltoncc13b352021-03-05 14:59:01 -0700668 return new Impl();
Chris Dalton133944a2018-11-16 23:30:29 -0500669}
670
Robert Phillipscad8fba2020-03-20 15:39:29 -0400671void FillRRectOp::onCreateProgramInfo(const GrCaps* caps,
672 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -0500673 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400674 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -0400675 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500676 GrXferBarrierFlags renderPassXferBarriers,
677 GrLoadOp colorLoadOp) {
Robert Phillips360ec182020-03-26 13:29:50 -0400678 GrGeometryProcessor* gp = Processor::Make(arena, fHelper.aaType(), fProcessorFlags);
Robert Phillipsce978572020-02-28 11:56:44 -0500679 SkASSERT(gp->instanceStride() == (size_t)fInstanceStride);
Chris Dalton133944a2018-11-16 23:30:29 -0500680
Brian Salomon8afde5f2020-04-01 16:22:00 -0400681 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -0400682 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -0500683 renderPassXferBarriers, colorLoadOp);
Robert Phillips8053c972019-11-21 10:44:53 -0500684}
685
Robert Phillips366176b2020-02-26 11:40:50 -0500686void FillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Robert Phillips8053c972019-11-21 10:44:53 -0500687 if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
688 return; // Setup failed.
689 }
690
691 if (!fProgramInfo) {
Robert Phillipscad8fba2020-03-20 15:39:29 -0400692 this->createProgramInfo(flushState);
Robert Phillips8053c972019-11-21 10:44:53 -0500693 }
Robert Phillips901aff02019-10-08 12:32:56 -0400694
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600695 flushState->bindPipelineAndScissorClip(*fProgramInfo, this->bounds());
696 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
Greg Daniel426274b2020-07-20 11:37:38 -0400697 flushState->bindBuffers(std::move(fIndexBuffer), std::move(fInstanceBuffer),
698 std::move(fVertexBuffer));
Chris Daltoncc13b352021-03-05 14:59:01 -0700699 flushState->drawIndexedInstanced(SK_ARRAY_COUNT(kIndexData), 0, fInstanceCount, fBaseInstance,
700 0);
Chris Dalton133944a2018-11-16 23:30:29 -0500701}
702
703// Will the given corner look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600704static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
Chris Dalton133944a2018-11-16 23:30:29 -0500705 Sk2f devRadii = devScale * cornerRadii;
706 if (devRadii[1] < devRadii[0]) {
707 devRadii = SkNx_shuffle<1,0>(devRadii);
708 }
Brian Osman788b9162020-02-07 10:36:46 -0500709 float minDevRadius = std::max(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
Chris Dalton133944a2018-11-16 23:30:29 -0500710 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
711 // This threshold was arrived at subjevtively on an NVIDIA chip.
712 return minDevRadius * minDevRadius * 5 > devRadii[1];
713}
714
Chris Dalton0dffbab2019-03-27 13:08:50 -0600715static bool can_use_hw_derivatives_with_coverage(
716 const Sk2f& devScale, const SkVector& cornerRadii) {
717 return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
Chris Dalton133944a2018-11-16 23:30:29 -0500718}
719
720// Will the given round rect look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600721static bool can_use_hw_derivatives_with_coverage(
722 const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
Chris Dalton133944a2018-11-16 23:30:29 -0500723 if (!shaderCaps.shaderDerivativeSupport()) {
724 return false;
725 }
726
727 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
728 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
729 Sk2f devScale = (x*x + y*y).sqrt();
730 switch (rrect.getType()) {
731 case SkRRect::kEmpty_Type:
732 case SkRRect::kRect_Type:
733 return true;
734
735 case SkRRect::kOval_Type:
736 case SkRRect::kSimple_Type:
Chris Dalton0dffbab2019-03-27 13:08:50 -0600737 return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
Chris Dalton133944a2018-11-16 23:30:29 -0500738
739 case SkRRect::kNinePatch_Type: {
740 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
741 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
742 Sk2f minRadii = Sk2f::Min(r0, r1);
743 Sk2f maxRadii = Sk2f::Max(r0, r1);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600744 return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
745 can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
Chris Dalton133944a2018-11-16 23:30:29 -0500746 }
747
748 case SkRRect::kComplex_Type: {
749 for (int i = 0; i < 4; ++i) {
750 auto corner = static_cast<SkRRect::Corner>(i);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600751 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
Chris Dalton133944a2018-11-16 23:30:29 -0500752 return false;
753 }
754 }
755 return true;
756 }
757 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600758 SK_ABORT("Invalid round rect type.");
Chris Dalton133944a2018-11-16 23:30:29 -0500759}
Robert Phillips366176b2020-02-26 11:40:50 -0500760
761} // anonymous namespace
762
763
Herb Derbyc76d4092020-10-07 16:46:15 -0400764GrOp::Owner GrFillRRectOp::Make(GrRecordingContext* ctx,
765 GrPaint&& paint,
766 const SkMatrix& viewMatrix,
767 const SkRRect& rrect,
768 GrAAType aaType) {
Robert Phillips360ec182020-03-26 13:29:50 -0400769 return FillRRectOp::Make(ctx, std::move(paint), viewMatrix, rrect, aaType);
Robert Phillips366176b2020-02-26 11:40:50 -0500770}
771
772#if GR_TEST_UTILS
773
774#include "src/gpu/GrDrawOpTest.h"
775
776GR_DRAW_OP_TEST_DEFINE(FillRRectOp) {
Robert Phillips366176b2020-02-26 11:40:50 -0500777 SkMatrix viewMatrix = GrTest::TestMatrix(random);
778 GrAAType aaType = GrAAType::kNone;
779 if (random->nextBool()) {
780 aaType = (numSamples > 1) ? GrAAType::kMSAA : GrAAType::kCoverage;
781 }
782
783 SkRect rect = GrTest::TestRect(random);
784 float w = rect.width();
785 float h = rect.height();
786
787 SkRRect rrect;
788 // TODO: test out other rrect configurations
789 rrect.setNinePatch(rect, w / 3.0f, h / 4.0f, w / 5.0f, h / 6.0);
790
791 return GrFillRRectOp::Make(context,
Robert Phillips360ec182020-03-26 13:29:50 -0400792 std::move(paint),
Robert Phillips366176b2020-02-26 11:40:50 -0500793 viewMatrix,
794 rrect,
Robert Phillips360ec182020-03-26 13:29:50 -0400795 aaType);
Robert Phillips366176b2020-02-26 11:40:50 -0500796}
797
798#endif