blob: bcd2e10d68ae45165efc31983e088f9c78cd38fe [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 Daltone3aec3c2021-03-05 21:46:18 +000067 kHasPerspective = 1 << 1,
68 kHasLocalCoords = 1 << 2,
69 kWideColor = 1 << 3
Robert Phillips366176b2020-02-26 11:40:50 -050070 };
71
Robert Phillips360ec182020-03-26 13:29:50 -040072 GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(ProcessorFlags);
Robert Phillips366176b2020-02-26 11:40:50 -050073
74 class Processor;
75
Herb Derbyc76d4092020-10-07 16:46:15 -040076 FillRRectOp(GrProcessorSet*,
Robert Phillips360ec182020-03-26 13:29:50 -040077 const SkPMColor4f& paintColor,
78 const SkMatrix& totalShapeMatrix,
79 const SkRRect&,
Chris Daltone3aec3c2021-03-05 21:46:18 +000080 GrAAType,
Robert Phillips360ec182020-03-26 13:29:50 -040081 ProcessorFlags,
82 const SkRect& devBounds);
Robert Phillips366176b2020-02-26 11:40:50 -050083
84 // These methods are used to append data of various POD types to our internal array of instance
85 // data. The actual layout of the instance buffer can vary from Op to Op.
86 template <typename T> inline T* appendInstanceData(int count) {
87 static_assert(std::is_pod<T>::value, "");
88 static_assert(4 == alignof(T), "");
89 return reinterpret_cast<T*>(fInstanceData.push_back_n(sizeof(T) * count));
90 }
91
92 template <typename T, typename... Args>
93 inline void writeInstanceData(const T& val, const Args&... remainder) {
94 memcpy(this->appendInstanceData<T>(1), &val, sizeof(T));
95 this->writeInstanceData(remainder...);
96 }
97
98 void writeInstanceData() {} // Halt condition.
99
Robert Phillipscad8fba2020-03-20 15:39:29 -0400100 GrProgramInfo* programInfo() final { return fProgramInfo; }
101
Robert Phillips366176b2020-02-26 11:40:50 -0500102 // Create a GrProgramInfo object in the provided arena
Robert Phillipscad8fba2020-03-20 15:39:29 -0400103 void onCreateProgramInfo(const GrCaps*,
104 SkArenaAlloc*,
Adlai Hollere2296f72020-11-19 13:41:26 -0500105 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400106 GrAppliedClip&&,
Greg Danield358cbe2020-09-11 09:33:54 -0400107 const GrXferProcessor::DstProxyView&,
Greg Daniel42dbca52020-11-20 10:22:43 -0500108 GrXferBarrierFlags renderPassXferBarriers,
109 GrLoadOp colorLoadOp) final;
Robert Phillips366176b2020-02-26 11:40:50 -0500110
Robert Phillips360ec182020-03-26 13:29:50 -0400111 Helper fHelper;
112 SkPMColor4f fColor;
113 const SkRect fLocalRect;
114 ProcessorFlags fProcessorFlags;
Robert Phillips366176b2020-02-26 11:40:50 -0500115
116 SkSTArray<sizeof(float) * 16 * 4, char, /*MEM_MOVE=*/ true> fInstanceData;
117 int fInstanceCount = 1;
118 int fInstanceStride = 0;
119
120 sk_sp<const GrBuffer> fInstanceBuffer;
121 sk_sp<const GrBuffer> fVertexBuffer;
122 sk_sp<const GrBuffer> fIndexBuffer;
123 int fBaseInstance = 0;
Chris Daltone3aec3c2021-03-05 21:46:18 +0000124 int fIndexCount = 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 Daltone3aec3c2021-03-05 21:46:18 +0000155 if (GrAAType::kCoverage == aaType) {
156 // TODO: Support perspective in a follow-on CL. This shouldn't be difficult, since we
157 // already use HW derivatives. The only trick will be adjusting the AA outset to account for
158 // perspective. (i.e., outset = 0.5 * z.)
159 if (viewMatrix.hasPerspective()) {
160 return nullptr;
161 }
162 if (can_use_hw_derivatives_with_coverage(*caps->shaderCaps(), viewMatrix, rrect)) {
163 // HW derivatives (more specifically, fwidth()) are consistently faster on all platforms
164 // in coverage mode. We use them as long as the approximation will be accurate enough.
165 flags |= ProcessorFlags::kUseHWDerivatives;
166 }
167 } else {
168 if (GrAAType::kMSAA == aaType) {
169 if (!caps->sampleLocationsSupport() || !caps->shaderCaps()->sampleMaskSupport() ||
170 caps->shaderCaps()->canOnlyUseSampleMaskWithStencil()) {
171 return nullptr;
172 }
173 }
174 if (viewMatrix.hasPerspective()) {
175 // HW derivatives are consistently slower on all platforms in sample mask mode. We
176 // therefore only use them when there is perspective, since then we can't interpolate
177 // the symbolic screen-space gradient.
178 flags |= ProcessorFlags::kUseHWDerivatives | ProcessorFlags::kHasPerspective;
179 }
Chris Dalton133944a2018-11-16 23:30:29 -0500180 }
181
182 // Produce a matrix that draws the round rect from normalized [-1, -1, +1, +1] space.
183 float l = rrect.rect().left(), r = rrect.rect().right(),
184 t = rrect.rect().top(), b = rrect.rect().bottom();
185 SkMatrix m;
186 // Unmap the normalized rect [-1, -1, +1, +1] back to [l, t, r, b].
187 m.setScaleTranslate((r - l)/2, (b - t)/2, (l + r)/2, (t + b)/2);
188 // Map to device space.
189 m.postConcat(viewMatrix);
190
Chris Dalton0dffbab2019-03-27 13:08:50 -0600191 SkRect devBounds;
Chris Daltone3aec3c2021-03-05 21:46:18 +0000192 if (!(flags & ProcessorFlags::kHasPerspective)) {
193 // Since m is an affine matrix that maps the rect [-1, -1, +1, +1] into the shape's
194 // device-space quad, it's quite simple to find the bounding rectangle:
195 devBounds = SkRect::MakeXYWH(m.getTranslateX(), m.getTranslateY(), 0, 0);
196 devBounds.outset(SkScalarAbs(m.getScaleX()) + SkScalarAbs(m.getSkewX()),
197 SkScalarAbs(m.getSkewY()) + SkScalarAbs(m.getScaleY()));
198 } else {
199 viewMatrix.mapRect(&devBounds, rrect.rect());
200 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600201
Chris Daltone3aec3c2021-03-05 21:46:18 +0000202 if (GrAAType::kMSAA == aaType && caps->preferTrianglesOverSampleMask()) {
203 // We are on a platform that prefers fine triangles instead of using the sample mask. See if
204 // the round rect is large enough that it will be faster for us to send it off to the
205 // default path renderer instead. The 200x200 threshold was arrived at using the
206 // "shapes_rrect" benchmark on an ARM Galaxy S9.
207 if (devBounds.height() * devBounds.width() > 200 * 200) {
208 return nullptr;
209 }
210 }
211
212 return Helper::FactoryHelper<FillRRectOp>(ctx, std::move(paint), m, rrect, aaType,
213 flags, devBounds);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600214}
215
Herb Derbyc76d4092020-10-07 16:46:15 -0400216FillRRectOp::FillRRectOp(GrProcessorSet* processorSet,
Robert Phillips360ec182020-03-26 13:29:50 -0400217 const SkPMColor4f& paintColor,
218 const SkMatrix& totalShapeMatrix,
219 const SkRRect& rrect,
Chris Daltone3aec3c2021-03-05 21:46:18 +0000220 GrAAType aaType,
Robert Phillips360ec182020-03-26 13:29:50 -0400221 ProcessorFlags processorFlags,
Robert Phillips366176b2020-02-26 11:40:50 -0500222 const SkRect& devBounds)
Robert Phillipscad8fba2020-03-20 15:39:29 -0400223 : INHERITED(ClassID())
Chris Daltone3aec3c2021-03-05 21:46:18 +0000224 , fHelper(processorSet, aaType)
Robert Phillips360ec182020-03-26 13:29:50 -0400225 , fColor(paintColor)
Chris Dalton0dffbab2019-03-27 13:08:50 -0600226 , fLocalRect(rrect.rect())
Robert Phillips360ec182020-03-26 13:29:50 -0400227 , fProcessorFlags(processorFlags & ~(ProcessorFlags::kHasLocalCoords |
Chris Daltone3aec3c2021-03-05 21:46:18 +0000228 ProcessorFlags::kWideColor)) {
229 SkASSERT((fProcessorFlags & ProcessorFlags::kHasPerspective) ==
230 totalShapeMatrix.hasPerspective());
Greg Daniel5faf4742019-10-01 15:14:44 -0400231 this->setBounds(devBounds, GrOp::HasAABloat::kYes, GrOp::IsHairline::kNo);
Chris Dalton133944a2018-11-16 23:30:29 -0500232
233 // Write the matrix attribs.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600234 const SkMatrix& m = totalShapeMatrix;
Chris Daltone3aec3c2021-03-05 21:46:18 +0000235 if (!(fProcessorFlags & ProcessorFlags::kHasPerspective)) {
236 // Affine 2D transformation (float2x2 plus float2 translate).
237 SkASSERT(!m.hasPerspective());
238 this->writeInstanceData(m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY());
239 this->writeInstanceData(m.getTranslateX(), m.getTranslateY());
240 } else {
241 // Perspective float3x3 transformation matrix.
242 SkASSERT(m.hasPerspective());
243 m.get9(this->appendInstanceData<float>(9));
244 }
Chris Dalton133944a2018-11-16 23:30:29 -0500245
246 // Convert the radii to [-1, -1, +1, +1] space and write their attribs.
247 Sk4f radiiX, radiiY;
248 Sk4f::Load2(SkRRectPriv::GetRadiiArray(rrect), &radiiX, &radiiY);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600249 (radiiX * (2/rrect.width())).store(this->appendInstanceData<float>(4));
250 (radiiY * (2/rrect.height())).store(this->appendInstanceData<float>(4));
Chris Dalton133944a2018-11-16 23:30:29 -0500251
252 // We will write the color and local rect attribs during finalize().
253}
254
Robert Phillips366176b2020-02-26 11:40:50 -0500255GrProcessorSet::Analysis FillRRectOp::finalize(
Chris Dalton6ce447a2019-06-23 18:07:38 -0600256 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
257 GrClampType clampType) {
Chris Dalton133944a2018-11-16 23:30:29 -0500258 SkASSERT(1 == fInstanceCount);
259
Robert Phillips360ec182020-03-26 13:29:50 -0400260 bool isWideColor;
261 auto analysis = fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
262 GrProcessorAnalysisCoverage::kSingleChannel,
263 &fColor, &isWideColor);
Chris Dalton133944a2018-11-16 23:30:29 -0500264
265 // Finish writing the instance attribs.
Robert Phillips360ec182020-03-26 13:29:50 -0400266 if (isWideColor) {
267 fProcessorFlags |= ProcessorFlags::kWideColor;
268 this->writeInstanceData(fColor);
Brian Osman5105d682019-02-13 16:06:14 -0500269 } else {
Robert Phillips360ec182020-03-26 13:29:50 -0400270 this->writeInstanceData(fColor.toBytes_RGBA());
Brian Osman5105d682019-02-13 16:06:14 -0500271 }
272
Chris Dalton133944a2018-11-16 23:30:29 -0500273 if (analysis.usesLocalCoords()) {
Robert Phillips360ec182020-03-26 13:29:50 -0400274 fProcessorFlags |= ProcessorFlags::kHasLocalCoords;
Chris Dalton133944a2018-11-16 23:30:29 -0500275 this->writeInstanceData(fLocalRect);
Chris Dalton133944a2018-11-16 23:30:29 -0500276 }
277 fInstanceStride = fInstanceData.count();
278
Chris Dalton4b62aed2019-01-15 11:53:00 -0700279 return analysis;
Chris Dalton133944a2018-11-16 23:30:29 -0500280}
281
Herb Derbye25c3002020-10-27 15:57:27 -0400282GrOp::CombineResult FillRRectOp::onCombineIfPossible(GrOp* op, SkArenaAlloc*, const GrCaps& caps) {
Robert Phillips366176b2020-02-26 11:40:50 -0500283 const auto& that = *op->cast<FillRRectOp>();
Robert Phillips360ec182020-03-26 13:29:50 -0400284 if (!fHelper.isCompatible(that.fHelper, caps, this->bounds(), that.bounds())) {
285 return CombineResult::kCannotCombine;
286 }
287
288 if (fProcessorFlags != that.fProcessorFlags ||
Chris Dalton133944a2018-11-16 23:30:29 -0500289 fInstanceData.count() > std::numeric_limits<int>::max() - that.fInstanceData.count()) {
290 return CombineResult::kCannotCombine;
291 }
292
293 fInstanceData.push_back_n(that.fInstanceData.count(), that.fInstanceData.begin());
294 fInstanceCount += that.fInstanceCount;
295 SkASSERT(fInstanceStride == that.fInstanceStride);
296 return CombineResult::kMerged;
297}
298
Robert Phillips366176b2020-02-26 11:40:50 -0500299class FillRRectOp::Processor : public GrGeometryProcessor {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600300public:
Robert Phillips360ec182020-03-26 13:29:50 -0400301 static GrGeometryProcessor* Make(SkArenaAlloc* arena, GrAAType aaType, ProcessorFlags flags) {
Mike Kleinf1241082020-12-14 15:59:09 -0600302 return arena->make([&](void* ptr) {
303 return new (ptr) Processor(aaType, flags);
304 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500305 }
306
Robert Phillips8053c972019-11-21 10:44:53 -0500307 const char* name() const final { return "GrFillRRectOp::Processor"; }
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500308
Robert Phillips8053c972019-11-21 10:44:53 -0500309 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const final {
Chris Daltone3aec3c2021-03-05 21:46:18 +0000310 b->addBits(4, (uint32_t)fFlags, "flags");
311 b->addBits(2, (uint32_t)fAAType, "aaType");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500312 }
313
Robert Phillips8053c972019-11-21 10:44:53 -0500314 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500315
316private:
Robert Phillips360ec182020-03-26 13:29:50 -0400317 Processor(GrAAType aaType, ProcessorFlags flags)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500318 : INHERITED(kGrFillRRectOp_Processor_ClassID)
Chris Daltone3aec3c2021-03-05 21:46:18 +0000319 , fAAType(aaType)
Chris Dalton0dffbab2019-03-27 13:08:50 -0600320 , fFlags(flags) {
Chris Daltone3aec3c2021-03-05 21:46:18 +0000321 int numVertexAttribs = (GrAAType::kCoverage == fAAType) ? 3 : 2;
322 this->setVertexAttributes(kVertexAttribs, numVertexAttribs);
Chris Dalton133944a2018-11-16 23:30:29 -0500323
Chris Daltone3aec3c2021-03-05 21:46:18 +0000324 if (!(fFlags & ProcessorFlags::kHasPerspective)) {
325 // Affine 2D transformation (float2x2 plus float2 translate).
326 fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
327 fInstanceAttribs.emplace_back(
328 "translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
329 } else {
330 // Perspective float3x3 transformation matrix.
331 fInstanceAttribs.emplace_back("persp_x", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
332 fInstanceAttribs.emplace_back("persp_y", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
333 fInstanceAttribs.emplace_back("persp_z", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
334 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600335 fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
336 fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
337 fColorAttrib = &fInstanceAttribs.push_back(
Robert Phillips360ec182020-03-26 13:29:50 -0400338 MakeColorAttribute("color", (fFlags & ProcessorFlags::kWideColor)));
339 if (fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600340 fInstanceAttribs.emplace_back(
341 "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
342 }
343 this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
Chris Daltone3aec3c2021-03-05 21:46:18 +0000344
345 if (GrAAType::kMSAA == fAAType) {
346 this->setWillUseCustomFeature(CustomFeatures::kSampleLocations);
347 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600348 }
349
Chris Dalton0dffbab2019-03-27 13:08:50 -0600350 static constexpr Attribute kVertexAttribs[] = {
351 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
352 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
353 // Coverage only.
354 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
355
Chris Daltone3aec3c2021-03-05 21:46:18 +0000356 const GrAAType fAAType;
Robert Phillips360ec182020-03-26 13:29:50 -0400357 const ProcessorFlags fFlags;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600358
359 SkSTArray<6, Attribute> fInstanceAttribs;
360 const Attribute* fColorAttrib;
361
Chris Daltone3aec3c2021-03-05 21:46:18 +0000362 class CoverageImpl;
363 class MSAAImpl;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500364
John Stiles7571f9e2020-09-02 22:42:33 -0400365 using INHERITED = GrGeometryProcessor;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600366};
367
Robert Phillips366176b2020-02-26 11:40:50 -0500368constexpr GrPrimitiveProcessor::Attribute FillRRectOp::Processor::kVertexAttribs[];
Chris Dalton0dffbab2019-03-27 13:08:50 -0600369
370// Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
Chris Dalton133944a2018-11-16 23:30:29 -0500371// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
372// edges. The Vertex struct tells the shader where to place its vertex within a normalized
373// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600374struct CoverageVertex {
Chris Dalton133944a2018-11-16 23:30:29 -0500375 std::array<float, 4> fRadiiSelector;
376 std::array<float, 2> fCorner;
377 std::array<float, 2> fRadiusOutset;
378 std::array<float, 2> fAABloatDirection;
379 float fCoverage;
380 float fIsLinearCoverage;
Chris Dalton133944a2018-11-16 23:30:29 -0500381};
382
383// This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
384// of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
385// rectangles.
386static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
387
Chris Daltone3aec3c2021-03-05 21:46:18 +0000388static constexpr CoverageVertex kCoverageVertexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500389 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700390 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
391 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500392
393 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700394 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
395 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500396
397 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700398 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
399 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500400
401 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700402 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
403 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500404
405
406 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700407 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
408 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500409
410 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700411 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
412 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500413
414 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700415 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
416 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500417
418 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700419 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
420 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500421
422
423 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700424 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
425 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
426 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
427 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
428 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
429 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500430
431 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700432 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
433 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
434 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
435 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
436 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
437 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500438
439 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700440 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
441 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
442 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
443 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
444 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
445 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500446
447 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700448 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
449 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
450 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
451 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
Chris Dalton2d07e862018-11-26 12:30:47 -0700452 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700453 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500454
Chris Daltone3aec3c2021-03-05 21:46:18 +0000455GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500456
Chris Daltone3aec3c2021-03-05 21:46:18 +0000457static constexpr uint16_t kCoverageIndexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500458 // Inset octagon (solid coverage).
459 0, 1, 7,
460 1, 2, 7,
461 7, 2, 6,
462 2, 3, 6,
463 6, 3, 5,
464 3, 4, 5,
465
466 // AA borders (linear coverage).
467 0, 1, 8, 1, 9, 8,
468 2, 3, 10, 3, 11, 10,
469 4, 5, 12, 5, 13, 12,
470 6, 7, 14, 7, 15, 14,
471
472 // Top-left arc.
473 16, 17, 21,
474 17, 21, 18,
475 21, 18, 20,
476 18, 20, 19,
477
478 // Top-right arc.
479 22, 23, 27,
480 23, 27, 24,
481 27, 24, 26,
482 24, 26, 25,
483
484 // Bottom-right arc.
485 28, 29, 33,
486 29, 33, 30,
487 33, 30, 32,
488 30, 32, 31,
489
490 // Bottom-left arc.
491 34, 35, 39,
492 35, 39, 36,
493 39, 36, 38,
494 36, 38, 37};
495
Chris Daltone3aec3c2021-03-05 21:46:18 +0000496GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
497
498
499// Our MSAA geometry consists of an inset octagon with full sample mask coverage, circumscribed
500// by a larger octagon that modifies the sample mask for the arc at each corresponding corner.
501struct MSAAVertex {
502 std::array<float, 4> fRadiiSelector;
503 std::array<float, 2> fCorner;
504 std::array<float, 2> fRadiusOutset;
505};
506
507static constexpr MSAAVertex kMSAAVertexData[] = {
508 // Left edge. (Negative radii selector indicates this is not an arc section.)
509 {{{0,0,0,-1}}, {{-1,+1}}, {{0,-1}}},
510 {{{-1,0,0,0}}, {{-1,-1}}, {{0,+1}}},
511
512 // Top edge.
513 {{{-1,0,0,0}}, {{-1,-1}}, {{+1,0}}},
514 {{{0,-1,0,0}}, {{+1,-1}}, {{-1,0}}},
515
516 // Right edge.
517 {{{0,-1,0,0}}, {{+1,-1}}, {{0,+1}}},
518 {{{0,0,-1,0}}, {{+1,+1}}, {{0,-1}}},
519
520 // Bottom edge.
521 {{{0,0,-1,0}}, {{+1,+1}}, {{-1,0}}},
522 {{{0,0,0,-1}}, {{-1,+1}}, {{+1,0}}},
523
524 // Top-left corner.
525 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}},
526 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}},
527 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}},
528 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}},
529
530 // Top-right corner.
531 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}},
532 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}},
533 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}},
534 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}},
535
536 // Bottom-right corner.
537 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}},
538 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}},
539 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}},
540 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}},
541
542 // Bottom-left corner.
543 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}},
544 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}},
545 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}},
546 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}}};
547
548GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
549
550static constexpr uint16_t kMSAAIndexData[] = {
551 // Inset octagon. (Full sample mask.)
552 0, 1, 2,
553 0, 2, 3,
554 0, 3, 6,
555 3, 4, 5,
556 3, 5, 6,
557 6, 7, 0,
558
559 // Top-left arc. (Sample mask is set to the arc.)
560 8, 9, 10,
561 9, 11, 10,
562
563 // Top-right arc.
564 12, 13, 14,
565 13, 15, 14,
566
567 // Bottom-right arc.
568 16, 17, 18,
569 17, 19, 18,
570
571 // Bottom-left arc.
572 20, 21, 22,
573 21, 23, 22};
574
575GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400576
Robert Phillipscad8fba2020-03-20 15:39:29 -0400577void FillRRectOp::onPrepareDraws(Target* target) {
578 if (void* instanceData = target->makeVertexSpace(fInstanceStride, fInstanceCount,
579 &fInstanceBuffer, &fBaseInstance)) {
Greg Danielf793de12019-09-05 13:23:23 -0400580 SkASSERT(fInstanceStride * fInstanceCount == fInstanceData.count());
581 memcpy(instanceData, fInstanceData.begin(), fInstanceData.count());
582 }
583
Chris Daltone3aec3c2021-03-05 21:46:18 +0000584 if (GrAAType::kCoverage == fHelper.aaType()) {
585 GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400586
Chris Daltone3aec3c2021-03-05 21:46:18 +0000587 fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
588 GrGpuBufferType::kIndex, sizeof(kCoverageIndexData), kCoverageIndexData,
589 gCoverageIndexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400590
Chris Daltone3aec3c2021-03-05 21:46:18 +0000591 GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
Greg Danielf793de12019-09-05 13:23:23 -0400592
Chris Daltone3aec3c2021-03-05 21:46:18 +0000593 fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
594 GrGpuBufferType::kVertex, sizeof(kCoverageVertexData), kCoverageVertexData,
595 gCoverageVertexBufferKey);
596
597 fIndexCount = SK_ARRAY_COUNT(kCoverageIndexData);
598 } else {
599 GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
600
601 fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
602 GrGpuBufferType::kIndex, sizeof(kMSAAIndexData), kMSAAIndexData,
603 gMSAAIndexBufferKey);
604
605 GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
606
607 fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
608 GrGpuBufferType::kVertex, sizeof(kMSAAVertexData), kMSAAVertexData,
609 gMSAAVertexBufferKey);
610
611 fIndexCount = SK_ARRAY_COUNT(kMSAAIndexData);
612 }
Greg Danielf793de12019-09-05 13:23:23 -0400613}
614
Chris Daltone3aec3c2021-03-05 21:46:18 +0000615class FillRRectOp::Processor::CoverageImpl : public GrGLSLGeometryProcessor {
Chris Dalton133944a2018-11-16 23:30:29 -0500616 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
617 const auto& proc = args.fGP.cast<Processor>();
Robert Phillips360ec182020-03-26 13:29:50 -0400618 bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
Chris Dalton133944a2018-11-16 23:30:29 -0500619
Chris Dalton0dffbab2019-03-27 13:08:50 -0600620 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
621
Chris Dalton133944a2018-11-16 23:30:29 -0500622 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
623 varyings->emitAttributes(proc);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600624 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500625 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
626
627 // Emit the vertex shader.
628 GrGLSLVertexBuilder* v = args.fVertBuilder;
629
630 // Unpack vertex attribs.
631 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
632 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
633 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
Chris Daltone3aec3c2021-03-05 21:46:18 +0000634 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
Chris Dalton133944a2018-11-16 23:30:29 -0500635 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
636
637 // Find the amount to bloat each edge for AA (in source space).
638 v->codeAppend("float2 pixellength = inversesqrt("
639 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
640 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
641 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
642 "abs(normalized_axis_dirs.zw));");
643 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
644
645 // Identify our radii.
Mike Reedd3efa992018-11-28 13:13:15 +0000646 v->codeAppend("float4 radii_and_neighbors = radii_selector"
647 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
648 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
649 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500650
651 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
Chris Daltone3aec3c2021-03-05 21:46:18 +0000652 // The rrect is more narrow than an AA coverage ramp. We can't draw as-is
653 // or else opposite AA borders will overlap. Instead, fudge the size up to
654 // the width of a coverage ramp, and then reduce total coverage to make
655 // the rect appear more thin.
Chris Dalton133944a2018-11-16 23:30:29 -0500656 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
Chris Daltone3aec3c2021-03-05 21:46:18 +0000657 v->codeAppend( "coverage /= max(aa_bloatradius.x, 1) * max(aa_bloatradius.y, 1);");
Chris Dalton133944a2018-11-16 23:30:29 -0500658 // Set radii to zero to ensure we take the "linear coverage" codepath.
659 // (The "coverage" variable only has effect in the linear codepath.)
660 v->codeAppend( "radii = float2(0);");
661 v->codeAppend("}");
662
Chris Daltone3aec3c2021-03-05 21:46:18 +0000663 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.25))) {");
Chris Dalton133944a2018-11-16 23:30:29 -0500664 // The radii are very small. Demote this arc to a sharp 90 degree corner.
Chris Daltone3aec3c2021-03-05 21:46:18 +0000665 v->codeAppend( "radii = aa_bloatradius;");
666 // Snap octagon vertices to the corner of the bounding box.
667 v->codeAppend( "radius_outset = floor(abs(radius_outset)) * radius_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500668 v->codeAppend( "is_linear_coverage = 1;");
669 v->codeAppend("} else {");
Chris Daltone3aec3c2021-03-05 21:46:18 +0000670 // Don't let radii get smaller than a pixel.
671 v->codeAppend( "radii = clamp(radii, pixellength, 2 - pixellength);");
672 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength, 2 - pixellength);");
Mike Reedd3efa992018-11-28 13:13:15 +0000673 // Don't let neighboring radii get closer together than 1/16 pixel.
674 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
675 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
676 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500677 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500678
679 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
680 // normalized [-1,-1,+1,+1] space.
Chris Daltone3aec3c2021-03-05 21:46:18 +0000681 v->codeAppend("float2 aa_outset = aa_bloat_direction.xy * aa_bloatradius;");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700682 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500683
Michael Ludwig553db622020-06-19 10:47:30 -0400684 // Write positions
Chris Dalton133944a2018-11-16 23:30:29 -0500685 GrShaderVar localCoord("", kFloat2_GrSLType);
Robert Phillips360ec182020-03-26 13:29:50 -0400686 if (proc.fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton133944a2018-11-16 23:30:29 -0500687 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
688 "local_rect.zw * (1 + vertexpos)) * .5;");
Michael Ludwig553db622020-06-19 10:47:30 -0400689 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
Chris Dalton133944a2018-11-16 23:30:29 -0500690 }
Chris Dalton133944a2018-11-16 23:30:29 -0500691
692 // Transform to device space.
Chris Daltone3aec3c2021-03-05 21:46:18 +0000693 SkASSERT(!(proc.fFlags & ProcessorFlags::kHasPerspective));
Chris Dalton133944a2018-11-16 23:30:29 -0500694 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
695 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
696 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
697
698 // Setup interpolants for coverage.
699 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
700 varyings->addVarying("arccoord", &arcCoord);
701 v->codeAppend("if (0 != is_linear_coverage) {");
702 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
703 // interpolate linear coverage across y.
Chris Daltone3aec3c2021-03-05 21:46:18 +0000704 v->codeAppendf( "%s.xy = float2(0, coverage);", arcCoord.vsOut());
Chris Dalton133944a2018-11-16 23:30:29 -0500705 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700706 // Find the normalized arc coordinates for our corner ellipse.
707 // (i.e., the coordinate system where x^2 + y^2 == 1).
708 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500709 // We are a corner piece: Interpolate the arc coordinates for coverage.
710 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
711 // instructs the fragment shader to use linear coverage).
712 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
713 if (!useHWDerivatives) {
714 // The gradient is order-1: Interpolate it across arccoord.zw.
715 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
716 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
717 }
718 v->codeAppend("}");
719
720 // Emit the fragment shader.
721 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
722
723 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
724 f->codeAppendf("half coverage;");
725 f->codeAppendf("if (0 == x_plus_1) {");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600726 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500727 f->codeAppendf("} else {");
728 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
729 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
730 if (useHWDerivatives) {
731 f->codeAppendf("float fnwidth = fwidth(fn);");
732 } else {
733 // The gradient is interpolated across arccoord.zw.
734 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
735 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
736 }
Chris Daltone3aec3c2021-03-05 21:46:18 +0000737 f->codeAppendf( "half d = half(fn/fnwidth);");
738 f->codeAppendf( "coverage = clamp(.5 - d, 0, 1);");
739 f->codeAppendf("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500740 f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage);
741 }
742
Brian Osman609f1592020-07-01 15:14:39 -0400743 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&) override {}
Chris Dalton133944a2018-11-16 23:30:29 -0500744};
745
Chris Dalton0dffbab2019-03-27 13:08:50 -0600746
Chris Daltone3aec3c2021-03-05 21:46:18 +0000747class FillRRectOp::Processor::MSAAImpl : public GrGLSLGeometryProcessor {
748 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
749 const auto& proc = args.fGP.cast<Processor>();
750 bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
751 bool hasPerspective = (proc.fFlags & ProcessorFlags::kHasPerspective);
752 bool hasLocalCoords = (proc.fFlags & ProcessorFlags::kHasLocalCoords);
753 SkASSERT(useHWDerivatives == hasPerspective);
754
755 SkASSERT(proc.vertexStride() == sizeof(MSAAVertex));
756
757 // Emit the vertex shader.
758 GrGLSLVertexBuilder* v = args.fVertBuilder;
759
760 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
761 varyings->emitAttributes(proc);
762 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
763 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
764
765 // Unpack vertex attribs.
766 v->codeAppendf("float2 corner = corner_and_radius_outsets.xy;");
767 v->codeAppendf("float2 radius_outset = corner_and_radius_outsets.zw;");
768
769 // Identify our radii.
770 v->codeAppend("float2 radii;");
771 v->codeAppend("radii.x = dot(radii_selector, radii_x);");
772 v->codeAppend("radii.y = dot(radii_selector, radii_y);");
773 v->codeAppendf("bool is_arc_section = (radii.x > 0);");
774 v->codeAppendf("radii = abs(radii);");
775
776 // Find our vertex position, adjusted for radii. Our rect is drawn in normalized
777 // [-1,-1,+1,+1] space.
778 v->codeAppend("float2 vertexpos = corner + radius_outset * radii;");
779
780 // Write positions
781 GrShaderVar localCoord("", kFloat2_GrSLType);
782 if (hasLocalCoords) {
783 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
784 "local_rect.zw * (1 + vertexpos)) * .5;");
785 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
786 }
787
788 // Transform to device space.
789 if (!hasPerspective) {
790 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
791 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
792 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
793 } else {
794 v->codeAppend("float3x3 persp_matrix = float3x3(persp_x, persp_y, persp_z);");
795 v->codeAppend("float3 devcoord = float3(vertexpos, 1) * persp_matrix;");
796 gpArgs->fPositionVar.set(kFloat3_GrSLType, "devcoord");
797 }
798
799 // Determine normalized arc coordinates for the implicit function.
800 GrGLSLVarying arcCoord((useHWDerivatives) ? kFloat2_GrSLType : kFloat4_GrSLType);
801 varyings->addVarying("arccoord", &arcCoord);
802 v->codeAppendf("if (is_arc_section) {");
803 v->codeAppendf( "%s.xy = 1 - abs(radius_outset);", arcCoord.vsOut());
804 if (!useHWDerivatives) {
805 // The gradient is order-1: Interpolate it across arccoord.zw.
806 // This doesn't work with perspective.
807 SkASSERT(!hasPerspective);
808 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
809 v->codeAppendf("%s.zw = derivatives * (%s.xy/radii * corner * 2);",
810 arcCoord.vsOut(), arcCoord.vsOut());
811 }
812 v->codeAppendf("} else {");
813 if (useHWDerivatives) {
814 v->codeAppendf("%s = float2(0);", arcCoord.vsOut());
815 } else {
816 v->codeAppendf("%s = float4(0);", arcCoord.vsOut());
817 }
818 v->codeAppendf("}");
819
820 // Emit the fragment shader.
821 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
822
823 f->codeAppendf("%s = half4(1);", args.fOutputCoverage);
824
825 // If x,y == 0, then we are drawing a triangle that does not track an arc.
826 f->codeAppendf("if (float2(0) != %s.xy) {", arcCoord.fsIn());
827 f->codeAppendf( "float fn = dot(%s.xy, %s.xy) - 1;", arcCoord.fsIn(), arcCoord.fsIn());
828 if (GrAAType::kMSAA == proc.fAAType) {
829 using ScopeFlags = GrGLSLFPFragmentBuilder::ScopeFlags;
830 if (!useHWDerivatives) {
831 f->codeAppendf("float2 grad = %s.zw;", arcCoord.fsIn());
832 f->applyFnToMultisampleMask("fn", "grad", ScopeFlags::kInsidePerPrimitiveBranch);
833 } else {
834 f->applyFnToMultisampleMask("fn", nullptr, ScopeFlags::kInsidePerPrimitiveBranch);
835 }
836 } else {
837 f->codeAppendf("if (fn > 0) {");
838 f->codeAppendf( "%s = half4(0);", args.fOutputCoverage);
839 f->codeAppendf("}");
840 }
841 f->codeAppendf("}");
842 }
843
844 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&) override {}
845};
846
Robert Phillips366176b2020-02-26 11:40:50 -0500847GrGLSLPrimitiveProcessor* FillRRectOp::Processor::createGLSLInstance(
Chris Dalton133944a2018-11-16 23:30:29 -0500848 const GrShaderCaps&) const {
Chris Daltone3aec3c2021-03-05 21:46:18 +0000849 if (GrAAType::kCoverage != fAAType) {
850 return new MSAAImpl();
851 }
852 return new CoverageImpl();
Chris Dalton133944a2018-11-16 23:30:29 -0500853}
854
Robert Phillipscad8fba2020-03-20 15:39:29 -0400855void FillRRectOp::onCreateProgramInfo(const GrCaps* caps,
856 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -0500857 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400858 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -0400859 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500860 GrXferBarrierFlags renderPassXferBarriers,
861 GrLoadOp colorLoadOp) {
Robert Phillips360ec182020-03-26 13:29:50 -0400862 GrGeometryProcessor* gp = Processor::Make(arena, fHelper.aaType(), fProcessorFlags);
Robert Phillipsce978572020-02-28 11:56:44 -0500863 SkASSERT(gp->instanceStride() == (size_t)fInstanceStride);
Chris Dalton133944a2018-11-16 23:30:29 -0500864
Brian Salomon8afde5f2020-04-01 16:22:00 -0400865 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -0400866 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -0500867 renderPassXferBarriers, colorLoadOp);
Robert Phillips8053c972019-11-21 10:44:53 -0500868}
869
Robert Phillips366176b2020-02-26 11:40:50 -0500870void FillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Robert Phillips8053c972019-11-21 10:44:53 -0500871 if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
872 return; // Setup failed.
873 }
874
875 if (!fProgramInfo) {
Robert Phillipscad8fba2020-03-20 15:39:29 -0400876 this->createProgramInfo(flushState);
Robert Phillips8053c972019-11-21 10:44:53 -0500877 }
Robert Phillips901aff02019-10-08 12:32:56 -0400878
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600879 flushState->bindPipelineAndScissorClip(*fProgramInfo, this->bounds());
880 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
Greg Daniel426274b2020-07-20 11:37:38 -0400881 flushState->bindBuffers(std::move(fIndexBuffer), std::move(fInstanceBuffer),
882 std::move(fVertexBuffer));
Chris Daltone3aec3c2021-03-05 21:46:18 +0000883 flushState->drawIndexedInstanced(fIndexCount, 0, fInstanceCount, fBaseInstance, 0);
Chris Dalton133944a2018-11-16 23:30:29 -0500884}
885
886// Will the given corner look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600887static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
Chris Dalton133944a2018-11-16 23:30:29 -0500888 Sk2f devRadii = devScale * cornerRadii;
889 if (devRadii[1] < devRadii[0]) {
890 devRadii = SkNx_shuffle<1,0>(devRadii);
891 }
Brian Osman788b9162020-02-07 10:36:46 -0500892 float minDevRadius = std::max(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
Chris Dalton133944a2018-11-16 23:30:29 -0500893 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
894 // This threshold was arrived at subjevtively on an NVIDIA chip.
895 return minDevRadius * minDevRadius * 5 > devRadii[1];
896}
897
Chris Dalton0dffbab2019-03-27 13:08:50 -0600898static bool can_use_hw_derivatives_with_coverage(
899 const Sk2f& devScale, const SkVector& cornerRadii) {
900 return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
Chris Dalton133944a2018-11-16 23:30:29 -0500901}
902
903// Will the given round rect look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600904static bool can_use_hw_derivatives_with_coverage(
905 const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
Chris Dalton133944a2018-11-16 23:30:29 -0500906 if (!shaderCaps.shaderDerivativeSupport()) {
907 return false;
908 }
909
910 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
911 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
912 Sk2f devScale = (x*x + y*y).sqrt();
913 switch (rrect.getType()) {
914 case SkRRect::kEmpty_Type:
915 case SkRRect::kRect_Type:
916 return true;
917
918 case SkRRect::kOval_Type:
919 case SkRRect::kSimple_Type:
Chris Dalton0dffbab2019-03-27 13:08:50 -0600920 return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
Chris Dalton133944a2018-11-16 23:30:29 -0500921
922 case SkRRect::kNinePatch_Type: {
923 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
924 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
925 Sk2f minRadii = Sk2f::Min(r0, r1);
926 Sk2f maxRadii = Sk2f::Max(r0, r1);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600927 return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
928 can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
Chris Dalton133944a2018-11-16 23:30:29 -0500929 }
930
931 case SkRRect::kComplex_Type: {
932 for (int i = 0; i < 4; ++i) {
933 auto corner = static_cast<SkRRect::Corner>(i);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600934 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
Chris Dalton133944a2018-11-16 23:30:29 -0500935 return false;
936 }
937 }
938 return true;
939 }
940 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600941 SK_ABORT("Invalid round rect type.");
Chris Dalton133944a2018-11-16 23:30:29 -0500942}
Robert Phillips366176b2020-02-26 11:40:50 -0500943
944} // anonymous namespace
945
946
Herb Derbyc76d4092020-10-07 16:46:15 -0400947GrOp::Owner GrFillRRectOp::Make(GrRecordingContext* ctx,
948 GrPaint&& paint,
949 const SkMatrix& viewMatrix,
950 const SkRRect& rrect,
951 GrAAType aaType) {
Robert Phillips360ec182020-03-26 13:29:50 -0400952 return FillRRectOp::Make(ctx, std::move(paint), viewMatrix, rrect, aaType);
Robert Phillips366176b2020-02-26 11:40:50 -0500953}
954
955#if GR_TEST_UTILS
956
957#include "src/gpu/GrDrawOpTest.h"
958
959GR_DRAW_OP_TEST_DEFINE(FillRRectOp) {
Robert Phillips366176b2020-02-26 11:40:50 -0500960 SkMatrix viewMatrix = GrTest::TestMatrix(random);
961 GrAAType aaType = GrAAType::kNone;
962 if (random->nextBool()) {
963 aaType = (numSamples > 1) ? GrAAType::kMSAA : GrAAType::kCoverage;
964 }
965
966 SkRect rect = GrTest::TestRect(random);
967 float w = rect.width();
968 float h = rect.height();
969
970 SkRRect rrect;
971 // TODO: test out other rrect configurations
972 rrect.setNinePatch(rect, w / 3.0f, h / 4.0f, w / 5.0f, h / 6.0);
973
974 return GrFillRRectOp::Make(context,
Robert Phillips360ec182020-03-26 13:29:50 -0400975 std::move(paint),
Robert Phillips366176b2020-02-26 11:40:50 -0500976 viewMatrix,
977 rrect,
Robert Phillips360ec182020-03-26 13:29:50 -0400978 aaType);
Robert Phillips366176b2020-02-26 11:40:50 -0500979}
980
981#endif