blob: 3131bf6ad894012fd4d8a3466a27fe8a752d40e8 [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,
67 kHasPerspective = 1 << 1,
68 kHasLocalCoords = 1 << 2,
69 kWideColor = 1 << 3
70 };
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&,
80 GrAAType,
81 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&,
108 GrXferBarrierFlags renderPassXferBarriers) final;
Robert Phillips366176b2020-02-26 11:40:50 -0500109
Robert Phillips360ec182020-03-26 13:29:50 -0400110 Helper fHelper;
111 SkPMColor4f fColor;
112 const SkRect fLocalRect;
113 ProcessorFlags fProcessorFlags;
Robert Phillips366176b2020-02-26 11:40:50 -0500114
115 SkSTArray<sizeof(float) * 16 * 4, char, /*MEM_MOVE=*/ true> fInstanceData;
116 int fInstanceCount = 1;
117 int fInstanceStride = 0;
118
119 sk_sp<const GrBuffer> fInstanceBuffer;
120 sk_sp<const GrBuffer> fVertexBuffer;
121 sk_sp<const GrBuffer> fIndexBuffer;
122 int fBaseInstance = 0;
123 int fIndexCount = 0;
124
125 // If this op is prePrepared the created programInfo will be stored here for use in
126 // onExecute. In the prePrepared case it will have been stored in the record-time arena.
127 GrProgramInfo* fProgramInfo = nullptr;
128
John Stiles7571f9e2020-09-02 22:42:33 -0400129 using INHERITED = GrMeshDrawOp;
Robert Phillips366176b2020-02-26 11:40:50 -0500130};
131
Robert Phillips360ec182020-03-26 13:29:50 -0400132GR_MAKE_BITFIELD_CLASS_OPS(FillRRectOp::ProcessorFlags)
Chris Dalton133944a2018-11-16 23:30:29 -0500133
134// Hardware derivatives are not always accurate enough for highly elliptical corners. This method
135// checks to make sure the corners will still all look good if we use HW derivatives.
Robert Phillips360ec182020-03-26 13:29:50 -0400136static bool can_use_hw_derivatives_with_coverage(const GrShaderCaps&,
137 const SkMatrix&,
138 const SkRRect&);
Chris Dalton133944a2018-11-16 23:30:29 -0500139
Herb Derbyc76d4092020-10-07 16:46:15 -0400140GrOp::Owner FillRRectOp::Make(GrRecordingContext* ctx,
141 GrPaint&& paint,
142 const SkMatrix& viewMatrix,
143 const SkRRect& rrect,
144 GrAAType aaType) {
Robert Phillips360ec182020-03-26 13:29:50 -0400145 using Helper = GrSimpleMeshDrawOpHelper;
146
147 const GrCaps* caps = ctx->priv().caps();
148
Chris Daltona77cdee2020-04-03 14:50:43 -0600149 if (!caps->drawInstancedSupport()) {
Chris Dalton133944a2018-11-16 23:30:29 -0500150 return nullptr;
151 }
152
Robert Phillips360ec182020-03-26 13:29:50 -0400153 ProcessorFlags flags = ProcessorFlags::kNone;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600154 if (GrAAType::kCoverage == aaType) {
155 // TODO: Support perspective in a follow-on CL. This shouldn't be difficult, since we
156 // already 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 }
Robert Phillips360ec182020-03-26 13:29:50 -0400161 if (can_use_hw_derivatives_with_coverage(*caps->shaderCaps(), viewMatrix, rrect)) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600162 // HW derivatives (more specifically, fwidth()) are consistently faster on all platforms
163 // in coverage mode. We use them as long as the approximation will be accurate enough.
Robert Phillips360ec182020-03-26 13:29:50 -0400164 flags |= ProcessorFlags::kUseHWDerivatives;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600165 }
166 } else {
167 if (GrAAType::kMSAA == aaType) {
Robert Phillips360ec182020-03-26 13:29:50 -0400168 if (!caps->sampleLocationsSupport() || !caps->shaderCaps()->sampleMaskSupport() ||
169 caps->shaderCaps()->canOnlyUseSampleMaskWithStencil()) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600170 return nullptr;
171 }
172 }
173 if (viewMatrix.hasPerspective()) {
174 // HW derivatives are consistently slower on all platforms in sample mask mode. We
175 // therefore only use them when there is perspective, since then we can't interpolate
176 // the symbolic screen-space gradient.
Robert Phillips360ec182020-03-26 13:29:50 -0400177 flags |= ProcessorFlags::kUseHWDerivatives | ProcessorFlags::kHasPerspective;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600178 }
Chris Dalton133944a2018-11-16 23:30:29 -0500179 }
180
181 // Produce a matrix that draws the round rect from normalized [-1, -1, +1, +1] space.
182 float l = rrect.rect().left(), r = rrect.rect().right(),
183 t = rrect.rect().top(), b = rrect.rect().bottom();
184 SkMatrix m;
185 // Unmap the normalized rect [-1, -1, +1, +1] back to [l, t, r, b].
186 m.setScaleTranslate((r - l)/2, (b - t)/2, (l + r)/2, (t + b)/2);
187 // Map to device space.
188 m.postConcat(viewMatrix);
189
Chris Dalton0dffbab2019-03-27 13:08:50 -0600190 SkRect devBounds;
Robert Phillips360ec182020-03-26 13:29:50 -0400191 if (!(flags & ProcessorFlags::kHasPerspective)) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600192 // Since m is an affine matrix that maps the rect [-1, -1, +1, +1] into the shape's
193 // device-space quad, it's quite simple to find the bounding rectangle:
194 devBounds = SkRect::MakeXYWH(m.getTranslateX(), m.getTranslateY(), 0, 0);
195 devBounds.outset(SkScalarAbs(m.getScaleX()) + SkScalarAbs(m.getSkewX()),
196 SkScalarAbs(m.getSkewY()) + SkScalarAbs(m.getScaleY()));
197 } else {
198 viewMatrix.mapRect(&devBounds, rrect.rect());
199 }
200
Robert Phillips360ec182020-03-26 13:29:50 -0400201 if (GrAAType::kMSAA == aaType && caps->preferTrianglesOverSampleMask()) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600202 // We are on a platform that prefers fine triangles instead of using the sample mask. See if
203 // the round rect is large enough that it will be faster for us to send it off to the
204 // default path renderer instead. The 200x200 threshold was arrived at using the
205 // "shapes_rrect" benchmark on an ARM Galaxy S9.
206 if (devBounds.height() * devBounds.width() > 200 * 200) {
207 return nullptr;
208 }
209 }
210
Robert Phillips360ec182020-03-26 13:29:50 -0400211 return Helper::FactoryHelper<FillRRectOp>(ctx, std::move(paint), m, rrect, aaType,
212 flags, devBounds);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600213}
214
Herb Derbyc76d4092020-10-07 16:46:15 -0400215FillRRectOp::FillRRectOp(GrProcessorSet* processorSet,
Robert Phillips360ec182020-03-26 13:29:50 -0400216 const SkPMColor4f& paintColor,
217 const SkMatrix& totalShapeMatrix,
218 const SkRRect& rrect,
219 GrAAType aaType,
220 ProcessorFlags processorFlags,
Robert Phillips366176b2020-02-26 11:40:50 -0500221 const SkRect& devBounds)
Robert Phillipscad8fba2020-03-20 15:39:29 -0400222 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -0400223 , fHelper(processorSet, aaType)
Robert Phillips360ec182020-03-26 13:29:50 -0400224 , fColor(paintColor)
Chris Dalton0dffbab2019-03-27 13:08:50 -0600225 , fLocalRect(rrect.rect())
Robert Phillips360ec182020-03-26 13:29:50 -0400226 , fProcessorFlags(processorFlags & ~(ProcessorFlags::kHasLocalCoords |
227 ProcessorFlags::kWideColor)) {
228 SkASSERT((fProcessorFlags & ProcessorFlags::kHasPerspective) ==
229 totalShapeMatrix.hasPerspective());
Greg Daniel5faf4742019-10-01 15:14:44 -0400230 this->setBounds(devBounds, GrOp::HasAABloat::kYes, GrOp::IsHairline::kNo);
Chris Dalton133944a2018-11-16 23:30:29 -0500231
232 // Write the matrix attribs.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600233 const SkMatrix& m = totalShapeMatrix;
Robert Phillips360ec182020-03-26 13:29:50 -0400234 if (!(fProcessorFlags & ProcessorFlags::kHasPerspective)) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600235 // Affine 2D transformation (float2x2 plus float2 translate).
236 SkASSERT(!m.hasPerspective());
237 this->writeInstanceData(m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY());
238 this->writeInstanceData(m.getTranslateX(), m.getTranslateY());
239 } else {
240 // Perspective float3x3 transformation matrix.
241 SkASSERT(m.hasPerspective());
242 m.get9(this->appendInstanceData<float>(9));
243 }
Chris Dalton133944a2018-11-16 23:30:29 -0500244
245 // Convert the radii to [-1, -1, +1, +1] space and write their attribs.
246 Sk4f radiiX, radiiY;
247 Sk4f::Load2(SkRRectPriv::GetRadiiArray(rrect), &radiiX, &radiiY);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600248 (radiiX * (2/rrect.width())).store(this->appendInstanceData<float>(4));
249 (radiiY * (2/rrect.height())).store(this->appendInstanceData<float>(4));
Chris Dalton133944a2018-11-16 23:30:29 -0500250
251 // We will write the color and local rect attribs during finalize().
252}
253
Robert Phillips366176b2020-02-26 11:40:50 -0500254GrProcessorSet::Analysis FillRRectOp::finalize(
Chris Dalton6ce447a2019-06-23 18:07:38 -0600255 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
256 GrClampType clampType) {
Chris Dalton133944a2018-11-16 23:30:29 -0500257 SkASSERT(1 == fInstanceCount);
258
Robert Phillips360ec182020-03-26 13:29:50 -0400259 bool isWideColor;
260 auto analysis = fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
261 GrProcessorAnalysisCoverage::kSingleChannel,
262 &fColor, &isWideColor);
Chris Dalton133944a2018-11-16 23:30:29 -0500263
264 // Finish writing the instance attribs.
Robert Phillips360ec182020-03-26 13:29:50 -0400265 if (isWideColor) {
266 fProcessorFlags |= ProcessorFlags::kWideColor;
267 this->writeInstanceData(fColor);
Brian Osman5105d682019-02-13 16:06:14 -0500268 } else {
Robert Phillips360ec182020-03-26 13:29:50 -0400269 this->writeInstanceData(fColor.toBytes_RGBA());
Brian Osman5105d682019-02-13 16:06:14 -0500270 }
271
Chris Dalton133944a2018-11-16 23:30:29 -0500272 if (analysis.usesLocalCoords()) {
Robert Phillips360ec182020-03-26 13:29:50 -0400273 fProcessorFlags |= ProcessorFlags::kHasLocalCoords;
Chris Dalton133944a2018-11-16 23:30:29 -0500274 this->writeInstanceData(fLocalRect);
Chris Dalton133944a2018-11-16 23:30:29 -0500275 }
276 fInstanceStride = fInstanceData.count();
277
Chris Dalton4b62aed2019-01-15 11:53:00 -0700278 return analysis;
Chris Dalton133944a2018-11-16 23:30:29 -0500279}
280
Herb Derbye25c3002020-10-27 15:57:27 -0400281GrOp::CombineResult FillRRectOp::onCombineIfPossible(GrOp* op, SkArenaAlloc*, const GrCaps& caps) {
Robert Phillips366176b2020-02-26 11:40:50 -0500282 const auto& that = *op->cast<FillRRectOp>();
Robert Phillips360ec182020-03-26 13:29:50 -0400283 if (!fHelper.isCompatible(that.fHelper, caps, this->bounds(), that.bounds())) {
284 return CombineResult::kCannotCombine;
285 }
286
287 if (fProcessorFlags != that.fProcessorFlags ||
Chris Dalton133944a2018-11-16 23:30:29 -0500288 fInstanceData.count() > std::numeric_limits<int>::max() - that.fInstanceData.count()) {
289 return CombineResult::kCannotCombine;
290 }
291
292 fInstanceData.push_back_n(that.fInstanceData.count(), that.fInstanceData.begin());
293 fInstanceCount += that.fInstanceCount;
294 SkASSERT(fInstanceStride == that.fInstanceStride);
295 return CombineResult::kMerged;
296}
297
Robert Phillips366176b2020-02-26 11:40:50 -0500298class FillRRectOp::Processor : public GrGeometryProcessor {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600299public:
Robert Phillips360ec182020-03-26 13:29:50 -0400300 static GrGeometryProcessor* Make(SkArenaAlloc* arena, GrAAType aaType, ProcessorFlags flags) {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500301 return arena->make<Processor>(aaType, flags);
302 }
303
Robert Phillips8053c972019-11-21 10:44:53 -0500304 const char* name() const final { return "GrFillRRectOp::Processor"; }
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500305
Robert Phillips8053c972019-11-21 10:44:53 -0500306 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const final {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500307 b->add32(((uint32_t)fFlags << 16) | (uint32_t)fAAType);
308 }
309
Robert Phillips8053c972019-11-21 10:44:53 -0500310 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500311
312private:
313 friend class ::SkArenaAlloc; // for access to ctor
314
Robert Phillips360ec182020-03-26 13:29:50 -0400315 Processor(GrAAType aaType, ProcessorFlags flags)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500316 : INHERITED(kGrFillRRectOp_Processor_ClassID)
Chris Dalton0dffbab2019-03-27 13:08:50 -0600317 , fAAType(aaType)
318 , fFlags(flags) {
319 int numVertexAttribs = (GrAAType::kCoverage == fAAType) ? 3 : 2;
320 this->setVertexAttributes(kVertexAttribs, numVertexAttribs);
Chris Dalton133944a2018-11-16 23:30:29 -0500321
Robert Phillips360ec182020-03-26 13:29:50 -0400322 if (!(fFlags & ProcessorFlags::kHasPerspective)) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600323 // Affine 2D transformation (float2x2 plus float2 translate).
324 fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
325 fInstanceAttribs.emplace_back(
326 "translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
327 } else {
328 // Perspective float3x3 transformation matrix.
329 fInstanceAttribs.emplace_back("persp_x", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
330 fInstanceAttribs.emplace_back("persp_y", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
331 fInstanceAttribs.emplace_back("persp_z", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
332 }
333 fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
334 fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
335 fColorAttrib = &fInstanceAttribs.push_back(
Robert Phillips360ec182020-03-26 13:29:50 -0400336 MakeColorAttribute("color", (fFlags & ProcessorFlags::kWideColor)));
337 if (fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600338 fInstanceAttribs.emplace_back(
339 "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
340 }
341 this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
342
343 if (GrAAType::kMSAA == fAAType) {
344 this->setWillUseCustomFeature(CustomFeatures::kSampleLocations);
345 }
346 }
347
Chris Dalton0dffbab2019-03-27 13:08:50 -0600348 static constexpr Attribute kVertexAttribs[] = {
349 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
350 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
351 // Coverage only.
352 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
353
Robert Phillips360ec182020-03-26 13:29:50 -0400354 const GrAAType fAAType;
355 const ProcessorFlags fFlags;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600356
357 SkSTArray<6, Attribute> fInstanceAttribs;
358 const Attribute* fColorAttrib;
359
360 class CoverageImpl;
361 class MSAAImpl;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500362
John Stiles7571f9e2020-09-02 22:42:33 -0400363 using INHERITED = GrGeometryProcessor;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600364};
365
Robert Phillips366176b2020-02-26 11:40:50 -0500366constexpr GrPrimitiveProcessor::Attribute FillRRectOp::Processor::kVertexAttribs[];
Chris Dalton0dffbab2019-03-27 13:08:50 -0600367
368// Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
Chris Dalton133944a2018-11-16 23:30:29 -0500369// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
370// edges. The Vertex struct tells the shader where to place its vertex within a normalized
371// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600372struct CoverageVertex {
Chris Dalton133944a2018-11-16 23:30:29 -0500373 std::array<float, 4> fRadiiSelector;
374 std::array<float, 2> fCorner;
375 std::array<float, 2> fRadiusOutset;
376 std::array<float, 2> fAABloatDirection;
377 float fCoverage;
378 float fIsLinearCoverage;
Chris Dalton133944a2018-11-16 23:30:29 -0500379};
380
381// This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
382// of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
383// rectangles.
384static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
385
Chris Dalton0dffbab2019-03-27 13:08:50 -0600386static constexpr CoverageVertex kCoverageVertexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500387 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700388 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
389 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500390
391 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700392 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
393 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500394
395 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700396 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
397 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500398
399 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700400 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
401 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500402
403
404 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700405 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
406 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500407
408 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700409 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
410 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500411
412 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700413 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
414 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500415
416 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700417 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
418 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500419
420
421 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700422 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
423 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
424 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
425 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
426 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
427 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500428
429 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700430 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
431 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
432 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
433 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
434 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
435 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500436
437 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700438 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
439 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
440 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
441 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
442 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
443 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500444
445 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700446 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
447 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
448 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
449 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
Chris Dalton2d07e862018-11-26 12:30:47 -0700450 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700451 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500452
Chris Dalton0dffbab2019-03-27 13:08:50 -0600453GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500454
Chris Dalton0dffbab2019-03-27 13:08:50 -0600455static constexpr uint16_t kCoverageIndexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500456 // Inset octagon (solid coverage).
457 0, 1, 7,
458 1, 2, 7,
459 7, 2, 6,
460 2, 3, 6,
461 6, 3, 5,
462 3, 4, 5,
463
464 // AA borders (linear coverage).
465 0, 1, 8, 1, 9, 8,
466 2, 3, 10, 3, 11, 10,
467 4, 5, 12, 5, 13, 12,
468 6, 7, 14, 7, 15, 14,
469
470 // Top-left arc.
471 16, 17, 21,
472 17, 21, 18,
473 21, 18, 20,
474 18, 20, 19,
475
476 // Top-right arc.
477 22, 23, 27,
478 23, 27, 24,
479 27, 24, 26,
480 24, 26, 25,
481
482 // Bottom-right arc.
483 28, 29, 33,
484 29, 33, 30,
485 33, 30, 32,
486 30, 32, 31,
487
488 // Bottom-left arc.
489 34, 35, 39,
490 35, 39, 36,
491 39, 36, 38,
492 36, 38, 37};
493
Chris Dalton0dffbab2019-03-27 13:08:50 -0600494GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500495
Greg Danielf793de12019-09-05 13:23:23 -0400496
497// Our MSAA geometry consists of an inset octagon with full sample mask coverage, circumscribed
498// by a larger octagon that modifies the sample mask for the arc at each corresponding corner.
499struct MSAAVertex {
500 std::array<float, 4> fRadiiSelector;
501 std::array<float, 2> fCorner;
502 std::array<float, 2> fRadiusOutset;
503};
504
505static constexpr MSAAVertex kMSAAVertexData[] = {
506 // Left edge. (Negative radii selector indicates this is not an arc section.)
507 {{{0,0,0,-1}}, {{-1,+1}}, {{0,-1}}},
508 {{{-1,0,0,0}}, {{-1,-1}}, {{0,+1}}},
509
510 // Top edge.
511 {{{-1,0,0,0}}, {{-1,-1}}, {{+1,0}}},
512 {{{0,-1,0,0}}, {{+1,-1}}, {{-1,0}}},
513
514 // Right edge.
515 {{{0,-1,0,0}}, {{+1,-1}}, {{0,+1}}},
516 {{{0,0,-1,0}}, {{+1,+1}}, {{0,-1}}},
517
518 // Bottom edge.
519 {{{0,0,-1,0}}, {{+1,+1}}, {{-1,0}}},
520 {{{0,0,0,-1}}, {{-1,+1}}, {{+1,0}}},
521
522 // Top-left corner.
523 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}},
524 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}},
525 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}},
526 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}},
527
528 // Top-right corner.
529 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}},
530 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}},
531 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}},
532 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}},
533
534 // Bottom-right corner.
535 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}},
536 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}},
537 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}},
538 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}},
539
540 // Bottom-left corner.
541 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}},
542 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}},
543 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}},
544 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}}};
545
546GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
547
548static constexpr uint16_t kMSAAIndexData[] = {
549 // Inset octagon. (Full sample mask.)
550 0, 1, 2,
551 0, 2, 3,
552 0, 3, 6,
553 3, 4, 5,
554 3, 5, 6,
555 6, 7, 0,
556
557 // Top-left arc. (Sample mask is set to the arc.)
558 8, 9, 10,
559 9, 11, 10,
560
561 // Top-right arc.
562 12, 13, 14,
563 13, 15, 14,
564
565 // Bottom-right arc.
566 16, 17, 18,
567 17, 19, 18,
568
569 // Bottom-left arc.
570 20, 21, 22,
571 21, 23, 22};
572
573GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
574
Robert Phillipscad8fba2020-03-20 15:39:29 -0400575void FillRRectOp::onPrepareDraws(Target* target) {
576 if (void* instanceData = target->makeVertexSpace(fInstanceStride, fInstanceCount,
577 &fInstanceBuffer, &fBaseInstance)) {
Greg Danielf793de12019-09-05 13:23:23 -0400578 SkASSERT(fInstanceStride * fInstanceCount == fInstanceData.count());
579 memcpy(instanceData, fInstanceData.begin(), fInstanceData.count());
580 }
581
Robert Phillips360ec182020-03-26 13:29:50 -0400582 if (GrAAType::kCoverage == fHelper.aaType()) {
Greg Danielf793de12019-09-05 13:23:23 -0400583 GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
584
Robert Phillipscad8fba2020-03-20 15:39:29 -0400585 fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
Greg Danielf793de12019-09-05 13:23:23 -0400586 GrGpuBufferType::kIndex, sizeof(kCoverageIndexData), kCoverageIndexData,
587 gCoverageIndexBufferKey);
588
589 GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
590
Robert Phillipscad8fba2020-03-20 15:39:29 -0400591 fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
Greg Danielf793de12019-09-05 13:23:23 -0400592 GrGpuBufferType::kVertex, sizeof(kCoverageVertexData), kCoverageVertexData,
593 gCoverageVertexBufferKey);
594
595 fIndexCount = SK_ARRAY_COUNT(kCoverageIndexData);
596 } else {
597 GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
598
Robert Phillipscad8fba2020-03-20 15:39:29 -0400599 fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
Greg Danielf793de12019-09-05 13:23:23 -0400600 GrGpuBufferType::kIndex, sizeof(kMSAAIndexData), kMSAAIndexData,
601 gMSAAIndexBufferKey);
602
603 GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
604
Robert Phillipscad8fba2020-03-20 15:39:29 -0400605 fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
Greg Danielf793de12019-09-05 13:23:23 -0400606 GrGpuBufferType::kVertex, sizeof(kMSAAVertexData), kMSAAVertexData,
607 gMSAAVertexBufferKey);
608
609 fIndexCount = SK_ARRAY_COUNT(kMSAAIndexData);
610 }
611}
612
Robert Phillips366176b2020-02-26 11:40:50 -0500613class FillRRectOp::Processor::CoverageImpl : public GrGLSLGeometryProcessor {
Chris Dalton133944a2018-11-16 23:30:29 -0500614 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
615 const auto& proc = args.fGP.cast<Processor>();
Robert Phillips360ec182020-03-26 13:29:50 -0400616 bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
Chris Dalton133944a2018-11-16 23:30:29 -0500617
Chris Dalton0dffbab2019-03-27 13:08:50 -0600618 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
619
Chris Dalton133944a2018-11-16 23:30:29 -0500620 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
621 varyings->emitAttributes(proc);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600622 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500623 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
624
625 // Emit the vertex shader.
626 GrGLSLVertexBuilder* v = args.fVertBuilder;
627
628 // Unpack vertex attribs.
629 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
630 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
631 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
632 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
633 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
634
635 // Find the amount to bloat each edge for AA (in source space).
636 v->codeAppend("float2 pixellength = inversesqrt("
637 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
638 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
639 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
640 "abs(normalized_axis_dirs.zw));");
641 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
642
643 // Identify our radii.
Mike Reedd3efa992018-11-28 13:13:15 +0000644 v->codeAppend("float4 radii_and_neighbors = radii_selector"
645 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
646 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
647 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500648
649 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
650 // The rrect is more narrow than an AA coverage ramp. We can't draw as-is
651 // or else opposite AA borders will overlap. Instead, fudge the size up to
652 // the width of a coverage ramp, and then reduce total coverage to make
653 // the rect appear more thin.
654 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
655 v->codeAppend( "coverage /= max(aa_bloatradius.x, 1) * max(aa_bloatradius.y, 1);");
656 // Set radii to zero to ensure we take the "linear coverage" codepath.
657 // (The "coverage" variable only has effect in the linear codepath.)
658 v->codeAppend( "radii = float2(0);");
659 v->codeAppend("}");
660
661 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.25))) {");
662 // The radii are very small. Demote this arc to a sharp 90 degree corner.
663 v->codeAppend( "radii = aa_bloatradius;");
664 // Snap octagon vertices to the corner of the bounding box.
665 v->codeAppend( "radius_outset = floor(abs(radius_outset)) * radius_outset;");
666 v->codeAppend( "is_linear_coverage = 1;");
667 v->codeAppend("} else {");
Mike Reedd3efa992018-11-28 13:13:15 +0000668 // Don't let radii get smaller than a pixel.
Chris Dalton133944a2018-11-16 23:30:29 -0500669 v->codeAppend( "radii = clamp(radii, pixellength, 2 - pixellength);");
Mike Reedd3efa992018-11-28 13:13:15 +0000670 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength, 2 - pixellength);");
671 // Don't let neighboring radii get closer together than 1/16 pixel.
672 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
673 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
674 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500675 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500676
677 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
678 // normalized [-1,-1,+1,+1] space.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700679 v->codeAppend("float2 aa_outset = aa_bloat_direction.xy * aa_bloatradius;");
680 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500681
Michael Ludwig553db622020-06-19 10:47:30 -0400682 // Write positions
Chris Dalton133944a2018-11-16 23:30:29 -0500683 GrShaderVar localCoord("", kFloat2_GrSLType);
Robert Phillips360ec182020-03-26 13:29:50 -0400684 if (proc.fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton133944a2018-11-16 23:30:29 -0500685 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
686 "local_rect.zw * (1 + vertexpos)) * .5;");
Michael Ludwig553db622020-06-19 10:47:30 -0400687 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
Chris Dalton133944a2018-11-16 23:30:29 -0500688 }
Chris Dalton133944a2018-11-16 23:30:29 -0500689
690 // Transform to device space.
Robert Phillips360ec182020-03-26 13:29:50 -0400691 SkASSERT(!(proc.fFlags & ProcessorFlags::kHasPerspective));
Chris Dalton133944a2018-11-16 23:30:29 -0500692 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
693 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
694 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
695
696 // Setup interpolants for coverage.
697 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
698 varyings->addVarying("arccoord", &arcCoord);
699 v->codeAppend("if (0 != is_linear_coverage) {");
700 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
701 // interpolate linear coverage across y.
702 v->codeAppendf( "%s.xy = float2(0, coverage);", arcCoord.vsOut());
703 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700704 // Find the normalized arc coordinates for our corner ellipse.
705 // (i.e., the coordinate system where x^2 + y^2 == 1).
706 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500707 // We are a corner piece: Interpolate the arc coordinates for coverage.
708 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
709 // instructs the fragment shader to use linear coverage).
710 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
711 if (!useHWDerivatives) {
712 // The gradient is order-1: Interpolate it across arccoord.zw.
713 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
714 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
715 }
716 v->codeAppend("}");
717
718 // Emit the fragment shader.
719 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
720
721 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
722 f->codeAppendf("half coverage;");
723 f->codeAppendf("if (0 == x_plus_1) {");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600724 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500725 f->codeAppendf("} else {");
726 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
727 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
728 if (useHWDerivatives) {
729 f->codeAppendf("float fnwidth = fwidth(fn);");
730 } else {
731 // The gradient is interpolated across arccoord.zw.
732 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
733 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
734 }
Ethan Nicholase1f55022019-02-05 17:17:40 -0500735 f->codeAppendf( "half d = half(fn/fnwidth);");
Chris Dalton133944a2018-11-16 23:30:29 -0500736 f->codeAppendf( "coverage = clamp(.5 - d, 0, 1);");
737 f->codeAppendf("}");
738 f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage);
739 }
740
Brian Osman609f1592020-07-01 15:14:39 -0400741 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&) override {}
Chris Dalton133944a2018-11-16 23:30:29 -0500742};
743
Chris Dalton0dffbab2019-03-27 13:08:50 -0600744
Robert Phillips366176b2020-02-26 11:40:50 -0500745class FillRRectOp::Processor::MSAAImpl : public GrGLSLGeometryProcessor {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600746 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
747 const auto& proc = args.fGP.cast<Processor>();
Robert Phillips360ec182020-03-26 13:29:50 -0400748 bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
749 bool hasPerspective = (proc.fFlags & ProcessorFlags::kHasPerspective);
750 bool hasLocalCoords = (proc.fFlags & ProcessorFlags::kHasLocalCoords);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600751 SkASSERT(useHWDerivatives == hasPerspective);
752
753 SkASSERT(proc.vertexStride() == sizeof(MSAAVertex));
754
755 // Emit the vertex shader.
756 GrGLSLVertexBuilder* v = args.fVertBuilder;
757
758 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
759 varyings->emitAttributes(proc);
760 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
761 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
762
763 // Unpack vertex attribs.
764 v->codeAppendf("float2 corner = corner_and_radius_outsets.xy;");
765 v->codeAppendf("float2 radius_outset = corner_and_radius_outsets.zw;");
766
767 // Identify our radii.
768 v->codeAppend("float2 radii;");
769 v->codeAppend("radii.x = dot(radii_selector, radii_x);");
770 v->codeAppend("radii.y = dot(radii_selector, radii_y);");
771 v->codeAppendf("bool is_arc_section = (radii.x > 0);");
772 v->codeAppendf("radii = abs(radii);");
773
774 // Find our vertex position, adjusted for radii. Our rect is drawn in normalized
775 // [-1,-1,+1,+1] space.
776 v->codeAppend("float2 vertexpos = corner + radius_outset * radii;");
777
Michael Ludwig553db622020-06-19 10:47:30 -0400778 // Write positions
Chris Dalton0dffbab2019-03-27 13:08:50 -0600779 GrShaderVar localCoord("", kFloat2_GrSLType);
780 if (hasLocalCoords) {
781 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
782 "local_rect.zw * (1 + vertexpos)) * .5;");
Michael Ludwig553db622020-06-19 10:47:30 -0400783 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600784 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600785
786 // Transform to device space.
787 if (!hasPerspective) {
788 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
789 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
790 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
791 } else {
792 v->codeAppend("float3x3 persp_matrix = float3x3(persp_x, persp_y, persp_z);");
793 v->codeAppend("float3 devcoord = float3(vertexpos, 1) * persp_matrix;");
794 gpArgs->fPositionVar.set(kFloat3_GrSLType, "devcoord");
795 }
796
797 // Determine normalized arc coordinates for the implicit function.
798 GrGLSLVarying arcCoord((useHWDerivatives) ? kFloat2_GrSLType : kFloat4_GrSLType);
799 varyings->addVarying("arccoord", &arcCoord);
800 v->codeAppendf("if (is_arc_section) {");
801 v->codeAppendf( "%s.xy = 1 - abs(radius_outset);", arcCoord.vsOut());
802 if (!useHWDerivatives) {
803 // The gradient is order-1: Interpolate it across arccoord.zw.
804 // This doesn't work with perspective.
805 SkASSERT(!hasPerspective);
806 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
807 v->codeAppendf("%s.zw = derivatives * (%s.xy/radii * corner * 2);",
808 arcCoord.vsOut(), arcCoord.vsOut());
809 }
810 v->codeAppendf("} else {");
811 if (useHWDerivatives) {
812 v->codeAppendf("%s = float2(0);", arcCoord.vsOut());
813 } else {
814 v->codeAppendf("%s = float4(0);", arcCoord.vsOut());
815 }
816 v->codeAppendf("}");
817
818 // Emit the fragment shader.
819 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
820
821 f->codeAppendf("%s = half4(1);", args.fOutputCoverage);
822
823 // If x,y == 0, then we are drawing a triangle that does not track an arc.
824 f->codeAppendf("if (float2(0) != %s.xy) {", arcCoord.fsIn());
825 f->codeAppendf( "float fn = dot(%s.xy, %s.xy) - 1;", arcCoord.fsIn(), arcCoord.fsIn());
826 if (GrAAType::kMSAA == proc.fAAType) {
827 using ScopeFlags = GrGLSLFPFragmentBuilder::ScopeFlags;
828 if (!useHWDerivatives) {
829 f->codeAppendf("float2 grad = %s.zw;", arcCoord.fsIn());
830 f->applyFnToMultisampleMask("fn", "grad", ScopeFlags::kInsidePerPrimitiveBranch);
831 } else {
832 f->applyFnToMultisampleMask("fn", nullptr, ScopeFlags::kInsidePerPrimitiveBranch);
833 }
834 } else {
835 f->codeAppendf("if (fn > 0) {");
836 f->codeAppendf( "%s = half4(0);", args.fOutputCoverage);
837 f->codeAppendf("}");
838 }
839 f->codeAppendf("}");
840 }
841
Brian Osman609f1592020-07-01 15:14:39 -0400842 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&) override {}
Chris Dalton0dffbab2019-03-27 13:08:50 -0600843};
844
Robert Phillips366176b2020-02-26 11:40:50 -0500845GrGLSLPrimitiveProcessor* FillRRectOp::Processor::createGLSLInstance(
Chris Dalton133944a2018-11-16 23:30:29 -0500846 const GrShaderCaps&) const {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600847 if (GrAAType::kCoverage != fAAType) {
848 return new MSAAImpl();
849 }
850 return new CoverageImpl();
Chris Dalton133944a2018-11-16 23:30:29 -0500851}
852
Robert Phillipscad8fba2020-03-20 15:39:29 -0400853void FillRRectOp::onCreateProgramInfo(const GrCaps* caps,
854 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -0500855 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400856 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -0400857 const GrXferProcessor::DstProxyView& dstProxyView,
858 GrXferBarrierFlags renderPassXferBarriers) {
Robert Phillips360ec182020-03-26 13:29:50 -0400859 GrGeometryProcessor* gp = Processor::Make(arena, fHelper.aaType(), fProcessorFlags);
Robert Phillipsce978572020-02-28 11:56:44 -0500860 SkASSERT(gp->instanceStride() == (size_t)fInstanceStride);
Chris Dalton133944a2018-11-16 23:30:29 -0500861
Brian Salomon8afde5f2020-04-01 16:22:00 -0400862 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -0400863 dstProxyView, gp, GrPrimitiveType::kTriangles,
864 renderPassXferBarriers);
Robert Phillips8053c972019-11-21 10:44:53 -0500865}
866
Robert Phillips366176b2020-02-26 11:40:50 -0500867void FillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Robert Phillips8053c972019-11-21 10:44:53 -0500868 if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
869 return; // Setup failed.
870 }
871
872 if (!fProgramInfo) {
Robert Phillipscad8fba2020-03-20 15:39:29 -0400873 this->createProgramInfo(flushState);
Robert Phillips8053c972019-11-21 10:44:53 -0500874 }
Robert Phillips901aff02019-10-08 12:32:56 -0400875
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600876 flushState->bindPipelineAndScissorClip(*fProgramInfo, this->bounds());
877 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
Greg Daniel426274b2020-07-20 11:37:38 -0400878 flushState->bindBuffers(std::move(fIndexBuffer), std::move(fInstanceBuffer),
879 std::move(fVertexBuffer));
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600880 flushState->drawIndexedInstanced(fIndexCount, 0, fInstanceCount, fBaseInstance, 0);
Chris Dalton133944a2018-11-16 23:30:29 -0500881}
882
883// Will the given corner look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600884static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
Chris Dalton133944a2018-11-16 23:30:29 -0500885 Sk2f devRadii = devScale * cornerRadii;
886 if (devRadii[1] < devRadii[0]) {
887 devRadii = SkNx_shuffle<1,0>(devRadii);
888 }
Brian Osman788b9162020-02-07 10:36:46 -0500889 float minDevRadius = std::max(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
Chris Dalton133944a2018-11-16 23:30:29 -0500890 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
891 // This threshold was arrived at subjevtively on an NVIDIA chip.
892 return minDevRadius * minDevRadius * 5 > devRadii[1];
893}
894
Chris Dalton0dffbab2019-03-27 13:08:50 -0600895static bool can_use_hw_derivatives_with_coverage(
896 const Sk2f& devScale, const SkVector& cornerRadii) {
897 return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
Chris Dalton133944a2018-11-16 23:30:29 -0500898}
899
900// Will the given round rect look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600901static bool can_use_hw_derivatives_with_coverage(
902 const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
Chris Dalton133944a2018-11-16 23:30:29 -0500903 if (!shaderCaps.shaderDerivativeSupport()) {
904 return false;
905 }
906
907 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
908 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
909 Sk2f devScale = (x*x + y*y).sqrt();
910 switch (rrect.getType()) {
911 case SkRRect::kEmpty_Type:
912 case SkRRect::kRect_Type:
913 return true;
914
915 case SkRRect::kOval_Type:
916 case SkRRect::kSimple_Type:
Chris Dalton0dffbab2019-03-27 13:08:50 -0600917 return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
Chris Dalton133944a2018-11-16 23:30:29 -0500918
919 case SkRRect::kNinePatch_Type: {
920 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
921 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
922 Sk2f minRadii = Sk2f::Min(r0, r1);
923 Sk2f maxRadii = Sk2f::Max(r0, r1);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600924 return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
925 can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
Chris Dalton133944a2018-11-16 23:30:29 -0500926 }
927
928 case SkRRect::kComplex_Type: {
929 for (int i = 0; i < 4; ++i) {
930 auto corner = static_cast<SkRRect::Corner>(i);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600931 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
Chris Dalton133944a2018-11-16 23:30:29 -0500932 return false;
933 }
934 }
935 return true;
936 }
937 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600938 SK_ABORT("Invalid round rect type.");
Chris Dalton133944a2018-11-16 23:30:29 -0500939}
Robert Phillips366176b2020-02-26 11:40:50 -0500940
941} // anonymous namespace
942
943
Herb Derbyc76d4092020-10-07 16:46:15 -0400944GrOp::Owner GrFillRRectOp::Make(GrRecordingContext* ctx,
945 GrPaint&& paint,
946 const SkMatrix& viewMatrix,
947 const SkRRect& rrect,
948 GrAAType aaType) {
Robert Phillips360ec182020-03-26 13:29:50 -0400949 return FillRRectOp::Make(ctx, std::move(paint), viewMatrix, rrect, aaType);
Robert Phillips366176b2020-02-26 11:40:50 -0500950}
951
952#if GR_TEST_UTILS
953
954#include "src/gpu/GrDrawOpTest.h"
955
956GR_DRAW_OP_TEST_DEFINE(FillRRectOp) {
Robert Phillips366176b2020-02-26 11:40:50 -0500957 SkMatrix viewMatrix = GrTest::TestMatrix(random);
958 GrAAType aaType = GrAAType::kNone;
959 if (random->nextBool()) {
960 aaType = (numSamples > 1) ? GrAAType::kMSAA : GrAAType::kCoverage;
961 }
962
963 SkRect rect = GrTest::TestRect(random);
964 float w = rect.width();
965 float h = rect.height();
966
967 SkRRect rrect;
968 // TODO: test out other rrect configurations
969 rrect.setNinePatch(rect, w / 3.0f, h / 4.0f, w / 5.0f, h / 6.0);
970
971 return GrFillRRectOp::Make(context,
Robert Phillips360ec182020-03-26 13:29:50 -0400972 std::move(paint),
Robert Phillips366176b2020-02-26 11:40:50 -0500973 viewMatrix,
974 rrect,
Robert Phillips360ec182020-03-26 13:29:50 -0400975 aaType);
Robert Phillips366176b2020-02-26 11:40:50 -0500976}
977
978#endif