blob: 3b56336d842dbf873d66df414a4090752d2a590d [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 Daltoncad44bc2021-03-05 09:30:36 -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 Daltoncad44bc2021-03-05 09:30:36 -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 Daltoncad44bc2021-03-05 09:30:36 -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 Daltoncad44bc2021-03-05 09:30:36 -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 Daltoncad44bc2021-03-05 09:30:36 -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 Daltoncad44bc2021-03-05 09:30:36 -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 Daltoncad44bc2021-03-05 09:30:36 -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 Daltoncad44bc2021-03-05 09:30:36 -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 Daltoncad44bc2021-03-05 09:30:36 -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 Daltoncad44bc2021-03-05 09:30:36 -0700289 this->setVertexAttributes(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
Chris Dalton133944a2018-11-16 23:30:29 -0500290
Chris Daltoncad44bc2021-03-05 09:30:36 -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 Daltoncad44bc2021-03-05 09:30:36 -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 Daltoncad44bc2021-03-05 09:30:36 -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 Daltoncad44bc2021-03-05 09:30:36 -0700407GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500408
Chris Daltoncad44bc2021-03-05 09:30:36 -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 Daltoncad44bc2021-03-05 09:30:36 -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 Daltoncad44bc2021-03-05 09:30:36 -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 Daltoncad44bc2021-03-05 09:30:36 -0700462 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400463
Chris Daltoncad44bc2021-03-05 09:30:36 -0700464 fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kIndex,
465 sizeof(kIndexData),
466 kIndexData, gIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400467
Chris Daltoncad44bc2021-03-05 09:30:36 -0700468 GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400469
Chris Daltoncad44bc2021-03-05 09:30:36 -0700470 fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,
471 sizeof(kVertexData),
472 kVertexData,
473 gVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400474}
475
Chris Daltoncad44bc2021-03-05 09:30:36 -0700476class FillRRectOp::Processor::Impl : public GrGLSLGeometryProcessor {
Chris Dalton133944a2018-11-16 23:30:29 -0500477 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
478 const auto& proc = args.fGP.cast<Processor>();
Robert Phillips360ec182020-03-26 13:29:50 -0400479 bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
Chris Dalton133944a2018-11-16 23:30:29 -0500480
Chris Dalton0dffbab2019-03-27 13:08:50 -0600481 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
482
Chris Dalton133944a2018-11-16 23:30:29 -0500483 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
484 varyings->emitAttributes(proc);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600485 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500486 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
487
488 // Emit the vertex shader.
489 GrGLSLVertexBuilder* v = args.fVertBuilder;
490
Chris Daltoncad44bc2021-03-05 09:30:36 -0700491 // When MSAA is enabled, we need to make sure every sample gets lit up on pixels that have
492 // fractional coverage. We do this by making the ramp wider.
493 v->codeAppendf("float aa_bloat_multiplier = %i;",
494 (proc.fFlags & ProcessorFlags::kMSAAEnabled)
495 ? 2 // Outset an entire pixel (2 radii).
496 : 1); // Outset one half pixel (1 radius).
497
Chris Dalton133944a2018-11-16 23:30:29 -0500498 // Unpack vertex attribs.
499 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
500 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
501 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
Chris Dalton133944a2018-11-16 23:30:29 -0500502 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
503
504 // Find the amount to bloat each edge for AA (in source space).
505 v->codeAppend("float2 pixellength = inversesqrt("
506 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
507 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
508 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
509 "abs(normalized_axis_dirs.zw));");
510 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
511
512 // Identify our radii.
Mike Reedd3efa992018-11-28 13:13:15 +0000513 v->codeAppend("float4 radii_and_neighbors = radii_selector"
514 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
515 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
516 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500517
Chris Daltoncad44bc2021-03-05 09:30:36 -0700518 v->codeAppend("float coverage_multiplier = 1;");
Chris Dalton133944a2018-11-16 23:30:29 -0500519 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
Chris Daltoncad44bc2021-03-05 09:30:36 -0700520 // The rrect is more narrow than a half-pixel AA coverage ramp. We can't
521 // draw as-is or else opposite AA borders will overlap. Instead, fudge the
522 // size up to the width of a coverage ramp, and then reduce total coverage
523 // to make the rect appear more thin.
Chris Dalton133944a2018-11-16 23:30:29 -0500524 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
Chris Daltoncad44bc2021-03-05 09:30:36 -0700525 v->codeAppend( "coverage_multiplier = 1 / (max(aa_bloatradius.x, 1) * "
526 "max(aa_bloatradius.y, 1));");
Chris Dalton133944a2018-11-16 23:30:29 -0500527 // Set radii to zero to ensure we take the "linear coverage" codepath.
528 // (The "coverage" variable only has effect in the linear codepath.)
529 v->codeAppend( "radii = float2(0);");
530 v->codeAppend("}");
531
Chris Daltoncad44bc2021-03-05 09:30:36 -0700532 // Unpack coverage.
533 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
534 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
535 // MSAA has a wider ramp that goes from -.5 to 1.5 instead of 0 to 1.
536 v->codeAppendf("coverage = (coverage - .5) * aa_bloat_multiplier + .5;");
537 }
538
539 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.5))) {");
Chris Dalton133944a2018-11-16 23:30:29 -0500540 // The radii are very small. Demote this arc to a sharp 90 degree corner.
Chris Daltoncad44bc2021-03-05 09:30:36 -0700541 v->codeAppend( "radii = float2(0);");
542 // Convert to a standard picture frame for an AA rect instead of the round
543 // rect geometry.
544 v->codeAppend( "aa_bloat_direction = sign(corner);");
545 v->codeAppend( "if (coverage > .5) {"); // Are we an inset edge?
546 v->codeAppend( "aa_bloat_direction = -aa_bloat_direction;");
547 v->codeAppend( "}");
Chris Dalton133944a2018-11-16 23:30:29 -0500548 v->codeAppend( "is_linear_coverage = 1;");
549 v->codeAppend("} else {");
Chris Daltoncad44bc2021-03-05 09:30:36 -0700550 // Don't let radii get smaller than a coverage ramp plus an extra half
551 // pixel for MSAA. Always use the same amount so we don't pop when
552 // switching between MSAA and coverage.
553 v->codeAppend( "radii = clamp(radii, pixellength * 1.5, 2 - pixellength * 1.5);");
554 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength * 1.5, "
555 "2 - pixellength * 1.5);");
Mike Reedd3efa992018-11-28 13:13:15 +0000556 // Don't let neighboring radii get closer together than 1/16 pixel.
557 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
558 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
559 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500560 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500561
562 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
563 // normalized [-1,-1,+1,+1] space.
Chris Daltoncad44bc2021-03-05 09:30:36 -0700564 v->codeAppend("float2 aa_outset = "
565 "aa_bloat_direction * aa_bloatradius * aa_bloat_multiplier;");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700566 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500567
Chris Daltoncad44bc2021-03-05 09:30:36 -0700568 v->codeAppend("if (coverage > .5) {"); // Are we an inset edge?
569 // Don't allow the aa insets to overlap. i.e., Don't let them inset past
570 // the center (x=y=0). Since we don't allow the rect to become thinner
571 // than 1px, this should only happen when using MSAA, where we inset by an
572 // entire pixel instead of half.
573 v->codeAppend( "if (aa_bloat_direction.x != 0 && vertexpos.x * corner.x < 0) {");
574 v->codeAppend( "float backset = abs(vertexpos.x);");
575 v->codeAppend( "vertexpos.x = 0;");
576 v->codeAppend( "vertexpos.y += "
577 "backset * sign(corner.y) * pixellength.y/pixellength.x;");
578 v->codeAppend( "coverage = (coverage - .5) * abs(corner.x) / "
579 "(abs(corner.x) + backset) + .5;");
580 v->codeAppend( "}");
581 v->codeAppend( "if (aa_bloat_direction.y != 0 && vertexpos.y * corner.y < 0) {");
582 v->codeAppend( "float backset = abs(vertexpos.y);");
583 v->codeAppend( "vertexpos.y = 0;");
584 v->codeAppend( "vertexpos.x += "
585 "backset * sign(corner.x) * pixellength.x/pixellength.y;");
586 v->codeAppend( "coverage = (coverage - .5) * abs(corner.y) / "
587 "(abs(corner.y) + backset) + .5;");
588 v->codeAppend( "}");
589 v->codeAppend("}");
590
Michael Ludwig553db622020-06-19 10:47:30 -0400591 // Write positions
Chris Dalton133944a2018-11-16 23:30:29 -0500592 GrShaderVar localCoord("", kFloat2_GrSLType);
Robert Phillips360ec182020-03-26 13:29:50 -0400593 if (proc.fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton133944a2018-11-16 23:30:29 -0500594 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
595 "local_rect.zw * (1 + vertexpos)) * .5;");
Michael Ludwig553db622020-06-19 10:47:30 -0400596 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
Chris Dalton133944a2018-11-16 23:30:29 -0500597 }
Chris Dalton133944a2018-11-16 23:30:29 -0500598
599 // Transform to device space.
600 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
601 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
602 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
603
604 // Setup interpolants for coverage.
605 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
606 varyings->addVarying("arccoord", &arcCoord);
607 v->codeAppend("if (0 != is_linear_coverage) {");
608 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
609 // interpolate linear coverage across y.
Chris Daltoncad44bc2021-03-05 09:30:36 -0700610 v->codeAppendf( "%s.xy = float2(0, coverage * coverage_multiplier);",
611 arcCoord.vsOut());
Chris Dalton133944a2018-11-16 23:30:29 -0500612 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700613 // Find the normalized arc coordinates for our corner ellipse.
614 // (i.e., the coordinate system where x^2 + y^2 == 1).
615 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500616 // We are a corner piece: Interpolate the arc coordinates for coverage.
617 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
618 // instructs the fragment shader to use linear coverage).
619 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
620 if (!useHWDerivatives) {
621 // The gradient is order-1: Interpolate it across arccoord.zw.
622 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
623 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
624 }
625 v->codeAppend("}");
626
627 // Emit the fragment shader.
628 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
629
630 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
631 f->codeAppendf("half coverage;");
632 f->codeAppendf("if (0 == x_plus_1) {");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600633 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500634 f->codeAppendf("} else {");
635 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
636 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
637 if (useHWDerivatives) {
638 f->codeAppendf("float fnwidth = fwidth(fn);");
639 } else {
640 // The gradient is interpolated across arccoord.zw.
641 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
642 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
643 }
Chris Daltoncad44bc2021-03-05 09:30:36 -0700644 f->codeAppendf( "coverage = .5 - half(fn/fnwidth);");
645 if (proc.fFlags & ProcessorFlags::kMSAAEnabled) {
646 // MSAA uses ramps larger than 1px, so we need to clamp in both branches.
647 f->codeAppendf("}");
648 }
649 f->codeAppendf("coverage = clamp(coverage, 0, 1);");
650 if (!(proc.fFlags & ProcessorFlags::kMSAAEnabled)) {
651 // When not using MSAA, we only need to clamp in the "arc" branch.
652 f->codeAppendf("}");
653 }
654 if (proc.fFlags & ProcessorFlags::kFakeNonAA) {
655 f->codeAppendf("coverage = round(coverage);");
656 }
Chris Dalton133944a2018-11-16 23:30:29 -0500657 f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage);
658 }
659
Brian Osman609f1592020-07-01 15:14:39 -0400660 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&) override {}
Chris Dalton133944a2018-11-16 23:30:29 -0500661};
662
Chris Dalton0dffbab2019-03-27 13:08:50 -0600663
Robert Phillips366176b2020-02-26 11:40:50 -0500664GrGLSLPrimitiveProcessor* FillRRectOp::Processor::createGLSLInstance(
Chris Dalton133944a2018-11-16 23:30:29 -0500665 const GrShaderCaps&) const {
Chris Daltoncad44bc2021-03-05 09:30:36 -0700666 return new Impl();
Chris Dalton133944a2018-11-16 23:30:29 -0500667}
668
Robert Phillipscad8fba2020-03-20 15:39:29 -0400669void FillRRectOp::onCreateProgramInfo(const GrCaps* caps,
670 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -0500671 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400672 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -0400673 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500674 GrXferBarrierFlags renderPassXferBarriers,
675 GrLoadOp colorLoadOp) {
Robert Phillips360ec182020-03-26 13:29:50 -0400676 GrGeometryProcessor* gp = Processor::Make(arena, fHelper.aaType(), fProcessorFlags);
Robert Phillipsce978572020-02-28 11:56:44 -0500677 SkASSERT(gp->instanceStride() == (size_t)fInstanceStride);
Chris Dalton133944a2018-11-16 23:30:29 -0500678
Brian Salomon8afde5f2020-04-01 16:22:00 -0400679 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -0400680 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -0500681 renderPassXferBarriers, colorLoadOp);
Robert Phillips8053c972019-11-21 10:44:53 -0500682}
683
Robert Phillips366176b2020-02-26 11:40:50 -0500684void FillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Robert Phillips8053c972019-11-21 10:44:53 -0500685 if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
686 return; // Setup failed.
687 }
688
689 if (!fProgramInfo) {
Robert Phillipscad8fba2020-03-20 15:39:29 -0400690 this->createProgramInfo(flushState);
Robert Phillips8053c972019-11-21 10:44:53 -0500691 }
Robert Phillips901aff02019-10-08 12:32:56 -0400692
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600693 flushState->bindPipelineAndScissorClip(*fProgramInfo, this->bounds());
694 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
Greg Daniel426274b2020-07-20 11:37:38 -0400695 flushState->bindBuffers(std::move(fIndexBuffer), std::move(fInstanceBuffer),
696 std::move(fVertexBuffer));
Chris Daltoncad44bc2021-03-05 09:30:36 -0700697 flushState->drawIndexedInstanced(SK_ARRAY_COUNT(kIndexData), 0, fInstanceCount, fBaseInstance,
698 0);
Chris Dalton133944a2018-11-16 23:30:29 -0500699}
700
701// Will the given corner look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600702static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
Chris Dalton133944a2018-11-16 23:30:29 -0500703 Sk2f devRadii = devScale * cornerRadii;
704 if (devRadii[1] < devRadii[0]) {
705 devRadii = SkNx_shuffle<1,0>(devRadii);
706 }
Brian Osman788b9162020-02-07 10:36:46 -0500707 float minDevRadius = std::max(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
Chris Dalton133944a2018-11-16 23:30:29 -0500708 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
709 // This threshold was arrived at subjevtively on an NVIDIA chip.
710 return minDevRadius * minDevRadius * 5 > devRadii[1];
711}
712
Chris Dalton0dffbab2019-03-27 13:08:50 -0600713static bool can_use_hw_derivatives_with_coverage(
714 const Sk2f& devScale, const SkVector& cornerRadii) {
715 return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
Chris Dalton133944a2018-11-16 23:30:29 -0500716}
717
718// Will the given round rect look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600719static bool can_use_hw_derivatives_with_coverage(
720 const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
Chris Dalton133944a2018-11-16 23:30:29 -0500721 if (!shaderCaps.shaderDerivativeSupport()) {
722 return false;
723 }
724
725 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
726 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
727 Sk2f devScale = (x*x + y*y).sqrt();
728 switch (rrect.getType()) {
729 case SkRRect::kEmpty_Type:
730 case SkRRect::kRect_Type:
731 return true;
732
733 case SkRRect::kOval_Type:
734 case SkRRect::kSimple_Type:
Chris Dalton0dffbab2019-03-27 13:08:50 -0600735 return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
Chris Dalton133944a2018-11-16 23:30:29 -0500736
737 case SkRRect::kNinePatch_Type: {
738 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
739 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
740 Sk2f minRadii = Sk2f::Min(r0, r1);
741 Sk2f maxRadii = Sk2f::Max(r0, r1);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600742 return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
743 can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
Chris Dalton133944a2018-11-16 23:30:29 -0500744 }
745
746 case SkRRect::kComplex_Type: {
747 for (int i = 0; i < 4; ++i) {
748 auto corner = static_cast<SkRRect::Corner>(i);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600749 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
Chris Dalton133944a2018-11-16 23:30:29 -0500750 return false;
751 }
752 }
753 return true;
754 }
755 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600756 SK_ABORT("Invalid round rect type.");
Chris Dalton133944a2018-11-16 23:30:29 -0500757}
Robert Phillips366176b2020-02-26 11:40:50 -0500758
759} // anonymous namespace
760
761
Herb Derbyc76d4092020-10-07 16:46:15 -0400762GrOp::Owner GrFillRRectOp::Make(GrRecordingContext* ctx,
763 GrPaint&& paint,
764 const SkMatrix& viewMatrix,
765 const SkRRect& rrect,
766 GrAAType aaType) {
Robert Phillips360ec182020-03-26 13:29:50 -0400767 return FillRRectOp::Make(ctx, std::move(paint), viewMatrix, rrect, aaType);
Robert Phillips366176b2020-02-26 11:40:50 -0500768}
769
770#if GR_TEST_UTILS
771
772#include "src/gpu/GrDrawOpTest.h"
773
774GR_DRAW_OP_TEST_DEFINE(FillRRectOp) {
Robert Phillips366176b2020-02-26 11:40:50 -0500775 SkMatrix viewMatrix = GrTest::TestMatrix(random);
776 GrAAType aaType = GrAAType::kNone;
777 if (random->nextBool()) {
778 aaType = (numSamples > 1) ? GrAAType::kMSAA : GrAAType::kCoverage;
779 }
780
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,
Robert Phillips360ec182020-03-26 13:29:50 -0400793 aaType);
Robert Phillips366176b2020-02-26 11:40:50 -0500794}
795
796#endif