blob: 62a132d99ba8c2198eac2101d44e5ad83345472b [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&,
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;
124 int fIndexCount = 0;
125
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 Dalton0dffbab2019-03-27 13:08:50 -0600155 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 }
Robert Phillips360ec182020-03-26 13:29:50 -0400162 if (can_use_hw_derivatives_with_coverage(*caps->shaderCaps(), viewMatrix, rrect)) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600163 // 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.
Robert Phillips360ec182020-03-26 13:29:50 -0400165 flags |= ProcessorFlags::kUseHWDerivatives;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600166 }
167 } else {
168 if (GrAAType::kMSAA == aaType) {
Robert Phillips360ec182020-03-26 13:29:50 -0400169 if (!caps->sampleLocationsSupport() || !caps->shaderCaps()->sampleMaskSupport() ||
170 caps->shaderCaps()->canOnlyUseSampleMaskWithStencil()) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600171 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.
Robert Phillips360ec182020-03-26 13:29:50 -0400178 flags |= ProcessorFlags::kUseHWDerivatives | ProcessorFlags::kHasPerspective;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600179 }
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;
Robert Phillips360ec182020-03-26 13:29:50 -0400192 if (!(flags & ProcessorFlags::kHasPerspective)) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600193 // 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 }
201
Robert Phillips360ec182020-03-26 13:29:50 -0400202 if (GrAAType::kMSAA == aaType && caps->preferTrianglesOverSampleMask()) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600203 // 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
Robert Phillips360ec182020-03-26 13:29:50 -0400212 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,
220 GrAAType aaType,
221 ProcessorFlags processorFlags,
Robert Phillips366176b2020-02-26 11:40:50 -0500222 const SkRect& devBounds)
Robert Phillipscad8fba2020-03-20 15:39:29 -0400223 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -0400224 , 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 |
228 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;
Robert Phillips360ec182020-03-26 13:29:50 -0400235 if (!(fProcessorFlags & ProcessorFlags::kHasPerspective)) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600236 // 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 {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500310 b->add32(((uint32_t)fFlags << 16) | (uint32_t)fAAType);
311 }
312
Robert Phillips8053c972019-11-21 10:44:53 -0500313 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500314
315private:
Robert Phillips360ec182020-03-26 13:29:50 -0400316 Processor(GrAAType aaType, ProcessorFlags flags)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500317 : INHERITED(kGrFillRRectOp_Processor_ClassID)
Chris Dalton0dffbab2019-03-27 13:08:50 -0600318 , fAAType(aaType)
319 , fFlags(flags) {
320 int numVertexAttribs = (GrAAType::kCoverage == fAAType) ? 3 : 2;
321 this->setVertexAttributes(kVertexAttribs, numVertexAttribs);
Chris Dalton133944a2018-11-16 23:30:29 -0500322
Robert Phillips360ec182020-03-26 13:29:50 -0400323 if (!(fFlags & ProcessorFlags::kHasPerspective)) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600324 // Affine 2D transformation (float2x2 plus float2 translate).
325 fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
326 fInstanceAttribs.emplace_back(
327 "translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
328 } else {
329 // Perspective float3x3 transformation matrix.
330 fInstanceAttribs.emplace_back("persp_x", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
331 fInstanceAttribs.emplace_back("persp_y", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
332 fInstanceAttribs.emplace_back("persp_z", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
333 }
334 fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
335 fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
336 fColorAttrib = &fInstanceAttribs.push_back(
Robert Phillips360ec182020-03-26 13:29:50 -0400337 MakeColorAttribute("color", (fFlags & ProcessorFlags::kWideColor)));
338 if (fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600339 fInstanceAttribs.emplace_back(
340 "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
341 }
342 this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
343
344 if (GrAAType::kMSAA == fAAType) {
345 this->setWillUseCustomFeature(CustomFeatures::kSampleLocations);
346 }
347 }
348
Chris Dalton0dffbab2019-03-27 13:08:50 -0600349 static constexpr Attribute kVertexAttribs[] = {
350 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
351 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
352 // Coverage only.
353 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
354
Robert Phillips360ec182020-03-26 13:29:50 -0400355 const GrAAType fAAType;
356 const ProcessorFlags fFlags;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600357
358 SkSTArray<6, Attribute> fInstanceAttribs;
359 const Attribute* fColorAttrib;
360
361 class CoverageImpl;
362 class MSAAImpl;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500363
John Stiles7571f9e2020-09-02 22:42:33 -0400364 using INHERITED = GrGeometryProcessor;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600365};
366
Robert Phillips366176b2020-02-26 11:40:50 -0500367constexpr GrPrimitiveProcessor::Attribute FillRRectOp::Processor::kVertexAttribs[];
Chris Dalton0dffbab2019-03-27 13:08:50 -0600368
369// Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
Chris Dalton133944a2018-11-16 23:30:29 -0500370// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
371// edges. The Vertex struct tells the shader where to place its vertex within a normalized
372// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600373struct CoverageVertex {
Chris Dalton133944a2018-11-16 23:30:29 -0500374 std::array<float, 4> fRadiiSelector;
375 std::array<float, 2> fCorner;
376 std::array<float, 2> fRadiusOutset;
377 std::array<float, 2> fAABloatDirection;
378 float fCoverage;
379 float fIsLinearCoverage;
Chris Dalton133944a2018-11-16 23:30:29 -0500380};
381
382// This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
383// of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
384// rectangles.
385static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
386
Chris Dalton0dffbab2019-03-27 13:08:50 -0600387static constexpr CoverageVertex kCoverageVertexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500388 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700389 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
390 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500391
392 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700393 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
394 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500395
396 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700397 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
398 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500399
400 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700401 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
402 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500403
404
405 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700406 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
407 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500408
409 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700410 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
411 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500412
413 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700414 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
415 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500416
417 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700418 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
419 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500420
421
422 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700423 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
424 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
425 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
426 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
427 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
428 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500429
430 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700431 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
432 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
433 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
434 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
435 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
436 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500437
438 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700439 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
440 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
441 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
442 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
443 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
444 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500445
446 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700447 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
448 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
449 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
450 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
Chris Dalton2d07e862018-11-26 12:30:47 -0700451 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700452 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500453
Chris Dalton0dffbab2019-03-27 13:08:50 -0600454GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500455
Chris Dalton0dffbab2019-03-27 13:08:50 -0600456static constexpr uint16_t kCoverageIndexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500457 // Inset octagon (solid coverage).
458 0, 1, 7,
459 1, 2, 7,
460 7, 2, 6,
461 2, 3, 6,
462 6, 3, 5,
463 3, 4, 5,
464
465 // AA borders (linear coverage).
466 0, 1, 8, 1, 9, 8,
467 2, 3, 10, 3, 11, 10,
468 4, 5, 12, 5, 13, 12,
469 6, 7, 14, 7, 15, 14,
470
471 // Top-left arc.
472 16, 17, 21,
473 17, 21, 18,
474 21, 18, 20,
475 18, 20, 19,
476
477 // Top-right arc.
478 22, 23, 27,
479 23, 27, 24,
480 27, 24, 26,
481 24, 26, 25,
482
483 // Bottom-right arc.
484 28, 29, 33,
485 29, 33, 30,
486 33, 30, 32,
487 30, 32, 31,
488
489 // Bottom-left arc.
490 34, 35, 39,
491 35, 39, 36,
492 39, 36, 38,
493 36, 38, 37};
494
Chris Dalton0dffbab2019-03-27 13:08:50 -0600495GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500496
Greg Danielf793de12019-09-05 13:23:23 -0400497
498// Our MSAA geometry consists of an inset octagon with full sample mask coverage, circumscribed
499// by a larger octagon that modifies the sample mask for the arc at each corresponding corner.
500struct MSAAVertex {
501 std::array<float, 4> fRadiiSelector;
502 std::array<float, 2> fCorner;
503 std::array<float, 2> fRadiusOutset;
504};
505
506static constexpr MSAAVertex kMSAAVertexData[] = {
507 // Left edge. (Negative radii selector indicates this is not an arc section.)
508 {{{0,0,0,-1}}, {{-1,+1}}, {{0,-1}}},
509 {{{-1,0,0,0}}, {{-1,-1}}, {{0,+1}}},
510
511 // Top edge.
512 {{{-1,0,0,0}}, {{-1,-1}}, {{+1,0}}},
513 {{{0,-1,0,0}}, {{+1,-1}}, {{-1,0}}},
514
515 // Right edge.
516 {{{0,-1,0,0}}, {{+1,-1}}, {{0,+1}}},
517 {{{0,0,-1,0}}, {{+1,+1}}, {{0,-1}}},
518
519 // Bottom edge.
520 {{{0,0,-1,0}}, {{+1,+1}}, {{-1,0}}},
521 {{{0,0,0,-1}}, {{-1,+1}}, {{+1,0}}},
522
523 // Top-left corner.
524 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}},
525 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}},
526 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}},
527 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}},
528
529 // Top-right corner.
530 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}},
531 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}},
532 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}},
533 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}},
534
535 // Bottom-right corner.
536 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}},
537 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}},
538 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}},
539 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}},
540
541 // Bottom-left corner.
542 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}},
543 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}},
544 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}},
545 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}}};
546
547GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
548
549static constexpr uint16_t kMSAAIndexData[] = {
550 // Inset octagon. (Full sample mask.)
551 0, 1, 2,
552 0, 2, 3,
553 0, 3, 6,
554 3, 4, 5,
555 3, 5, 6,
556 6, 7, 0,
557
558 // Top-left arc. (Sample mask is set to the arc.)
559 8, 9, 10,
560 9, 11, 10,
561
562 // Top-right arc.
563 12, 13, 14,
564 13, 15, 14,
565
566 // Bottom-right arc.
567 16, 17, 18,
568 17, 19, 18,
569
570 // Bottom-left arc.
571 20, 21, 22,
572 21, 23, 22};
573
574GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
575
Robert Phillipscad8fba2020-03-20 15:39:29 -0400576void FillRRectOp::onPrepareDraws(Target* target) {
577 if (void* instanceData = target->makeVertexSpace(fInstanceStride, fInstanceCount,
578 &fInstanceBuffer, &fBaseInstance)) {
Greg Danielf793de12019-09-05 13:23:23 -0400579 SkASSERT(fInstanceStride * fInstanceCount == fInstanceData.count());
580 memcpy(instanceData, fInstanceData.begin(), fInstanceData.count());
581 }
582
Robert Phillips360ec182020-03-26 13:29:50 -0400583 if (GrAAType::kCoverage == fHelper.aaType()) {
Greg Danielf793de12019-09-05 13:23:23 -0400584 GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
585
Robert Phillipscad8fba2020-03-20 15:39:29 -0400586 fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
Greg Danielf793de12019-09-05 13:23:23 -0400587 GrGpuBufferType::kIndex, sizeof(kCoverageIndexData), kCoverageIndexData,
588 gCoverageIndexBufferKey);
589
590 GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
591
Robert Phillipscad8fba2020-03-20 15:39:29 -0400592 fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
Greg Danielf793de12019-09-05 13:23:23 -0400593 GrGpuBufferType::kVertex, sizeof(kCoverageVertexData), kCoverageVertexData,
594 gCoverageVertexBufferKey);
595
596 fIndexCount = SK_ARRAY_COUNT(kCoverageIndexData);
597 } else {
598 GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
599
Robert Phillipscad8fba2020-03-20 15:39:29 -0400600 fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
Greg Danielf793de12019-09-05 13:23:23 -0400601 GrGpuBufferType::kIndex, sizeof(kMSAAIndexData), kMSAAIndexData,
602 gMSAAIndexBufferKey);
603
604 GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
605
Robert Phillipscad8fba2020-03-20 15:39:29 -0400606 fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
Greg Danielf793de12019-09-05 13:23:23 -0400607 GrGpuBufferType::kVertex, sizeof(kMSAAVertexData), kMSAAVertexData,
608 gMSAAVertexBufferKey);
609
610 fIndexCount = SK_ARRAY_COUNT(kMSAAIndexData);
611 }
612}
613
Robert Phillips366176b2020-02-26 11:40:50 -0500614class FillRRectOp::Processor::CoverageImpl : public GrGLSLGeometryProcessor {
Chris Dalton133944a2018-11-16 23:30:29 -0500615 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
616 const auto& proc = args.fGP.cast<Processor>();
Robert Phillips360ec182020-03-26 13:29:50 -0400617 bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
Chris Dalton133944a2018-11-16 23:30:29 -0500618
Chris Dalton0dffbab2019-03-27 13:08:50 -0600619 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
620
Chris Dalton133944a2018-11-16 23:30:29 -0500621 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
622 varyings->emitAttributes(proc);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600623 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500624 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
625
626 // Emit the vertex shader.
627 GrGLSLVertexBuilder* v = args.fVertBuilder;
628
629 // Unpack vertex attribs.
630 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
631 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
632 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
633 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
634 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
635
636 // Find the amount to bloat each edge for AA (in source space).
637 v->codeAppend("float2 pixellength = inversesqrt("
638 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
639 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
640 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
641 "abs(normalized_axis_dirs.zw));");
642 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
643
644 // Identify our radii.
Mike Reedd3efa992018-11-28 13:13:15 +0000645 v->codeAppend("float4 radii_and_neighbors = radii_selector"
646 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
647 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
648 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500649
650 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
651 // The rrect is more narrow than an AA coverage ramp. We can't draw as-is
652 // or else opposite AA borders will overlap. Instead, fudge the size up to
653 // the width of a coverage ramp, and then reduce total coverage to make
654 // the rect appear more thin.
655 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
656 v->codeAppend( "coverage /= max(aa_bloatradius.x, 1) * max(aa_bloatradius.y, 1);");
657 // Set radii to zero to ensure we take the "linear coverage" codepath.
658 // (The "coverage" variable only has effect in the linear codepath.)
659 v->codeAppend( "radii = float2(0);");
660 v->codeAppend("}");
661
662 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.25))) {");
663 // The radii are very small. Demote this arc to a sharp 90 degree corner.
664 v->codeAppend( "radii = aa_bloatradius;");
665 // Snap octagon vertices to the corner of the bounding box.
666 v->codeAppend( "radius_outset = floor(abs(radius_outset)) * radius_outset;");
667 v->codeAppend( "is_linear_coverage = 1;");
668 v->codeAppend("} else {");
Mike Reedd3efa992018-11-28 13:13:15 +0000669 // Don't let radii get smaller than a pixel.
Chris Dalton133944a2018-11-16 23:30:29 -0500670 v->codeAppend( "radii = clamp(radii, pixellength, 2 - pixellength);");
Mike Reedd3efa992018-11-28 13:13:15 +0000671 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength, 2 - pixellength);");
672 // Don't let neighboring radii get closer together than 1/16 pixel.
673 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
674 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
675 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500676 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500677
678 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
679 // normalized [-1,-1,+1,+1] space.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700680 v->codeAppend("float2 aa_outset = aa_bloat_direction.xy * aa_bloatradius;");
681 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500682
Michael Ludwig553db622020-06-19 10:47:30 -0400683 // Write positions
Chris Dalton133944a2018-11-16 23:30:29 -0500684 GrShaderVar localCoord("", kFloat2_GrSLType);
Robert Phillips360ec182020-03-26 13:29:50 -0400685 if (proc.fFlags & ProcessorFlags::kHasLocalCoords) {
Chris Dalton133944a2018-11-16 23:30:29 -0500686 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
687 "local_rect.zw * (1 + vertexpos)) * .5;");
Michael Ludwig553db622020-06-19 10:47:30 -0400688 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
Chris Dalton133944a2018-11-16 23:30:29 -0500689 }
Chris Dalton133944a2018-11-16 23:30:29 -0500690
691 // Transform to device space.
Robert Phillips360ec182020-03-26 13:29:50 -0400692 SkASSERT(!(proc.fFlags & ProcessorFlags::kHasPerspective));
Chris Dalton133944a2018-11-16 23:30:29 -0500693 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
694 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
695 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
696
697 // Setup interpolants for coverage.
698 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
699 varyings->addVarying("arccoord", &arcCoord);
700 v->codeAppend("if (0 != is_linear_coverage) {");
701 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
702 // interpolate linear coverage across y.
703 v->codeAppendf( "%s.xy = float2(0, coverage);", arcCoord.vsOut());
704 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700705 // Find the normalized arc coordinates for our corner ellipse.
706 // (i.e., the coordinate system where x^2 + y^2 == 1).
707 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500708 // We are a corner piece: Interpolate the arc coordinates for coverage.
709 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
710 // instructs the fragment shader to use linear coverage).
711 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
712 if (!useHWDerivatives) {
713 // The gradient is order-1: Interpolate it across arccoord.zw.
714 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
715 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
716 }
717 v->codeAppend("}");
718
719 // Emit the fragment shader.
720 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
721
722 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
723 f->codeAppendf("half coverage;");
724 f->codeAppendf("if (0 == x_plus_1) {");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600725 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500726 f->codeAppendf("} else {");
727 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
728 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
729 if (useHWDerivatives) {
730 f->codeAppendf("float fnwidth = fwidth(fn);");
731 } else {
732 // The gradient is interpolated across arccoord.zw.
733 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
734 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
735 }
Ethan Nicholase1f55022019-02-05 17:17:40 -0500736 f->codeAppendf( "half d = half(fn/fnwidth);");
Chris Dalton133944a2018-11-16 23:30:29 -0500737 f->codeAppendf( "coverage = clamp(.5 - d, 0, 1);");
738 f->codeAppendf("}");
739 f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage);
740 }
741
Brian Osman609f1592020-07-01 15:14:39 -0400742 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&) override {}
Chris Dalton133944a2018-11-16 23:30:29 -0500743};
744
Chris Dalton0dffbab2019-03-27 13:08:50 -0600745
Robert Phillips366176b2020-02-26 11:40:50 -0500746class FillRRectOp::Processor::MSAAImpl : public GrGLSLGeometryProcessor {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600747 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
748 const auto& proc = args.fGP.cast<Processor>();
Robert Phillips360ec182020-03-26 13:29:50 -0400749 bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives);
750 bool hasPerspective = (proc.fFlags & ProcessorFlags::kHasPerspective);
751 bool hasLocalCoords = (proc.fFlags & ProcessorFlags::kHasLocalCoords);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600752 SkASSERT(useHWDerivatives == hasPerspective);
753
754 SkASSERT(proc.vertexStride() == sizeof(MSAAVertex));
755
756 // Emit the vertex shader.
757 GrGLSLVertexBuilder* v = args.fVertBuilder;
758
759 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
760 varyings->emitAttributes(proc);
761 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
762 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
763
764 // Unpack vertex attribs.
765 v->codeAppendf("float2 corner = corner_and_radius_outsets.xy;");
766 v->codeAppendf("float2 radius_outset = corner_and_radius_outsets.zw;");
767
768 // Identify our radii.
769 v->codeAppend("float2 radii;");
770 v->codeAppend("radii.x = dot(radii_selector, radii_x);");
771 v->codeAppend("radii.y = dot(radii_selector, radii_y);");
772 v->codeAppendf("bool is_arc_section = (radii.x > 0);");
773 v->codeAppendf("radii = abs(radii);");
774
775 // Find our vertex position, adjusted for radii. Our rect is drawn in normalized
776 // [-1,-1,+1,+1] space.
777 v->codeAppend("float2 vertexpos = corner + radius_outset * radii;");
778
Michael Ludwig553db622020-06-19 10:47:30 -0400779 // Write positions
Chris Dalton0dffbab2019-03-27 13:08:50 -0600780 GrShaderVar localCoord("", kFloat2_GrSLType);
781 if (hasLocalCoords) {
782 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
783 "local_rect.zw * (1 + vertexpos)) * .5;");
Michael Ludwig553db622020-06-19 10:47:30 -0400784 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600785 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600786
787 // Transform to device space.
788 if (!hasPerspective) {
789 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
790 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
791 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
792 } else {
793 v->codeAppend("float3x3 persp_matrix = float3x3(persp_x, persp_y, persp_z);");
794 v->codeAppend("float3 devcoord = float3(vertexpos, 1) * persp_matrix;");
795 gpArgs->fPositionVar.set(kFloat3_GrSLType, "devcoord");
796 }
797
798 // Determine normalized arc coordinates for the implicit function.
799 GrGLSLVarying arcCoord((useHWDerivatives) ? kFloat2_GrSLType : kFloat4_GrSLType);
800 varyings->addVarying("arccoord", &arcCoord);
801 v->codeAppendf("if (is_arc_section) {");
802 v->codeAppendf( "%s.xy = 1 - abs(radius_outset);", arcCoord.vsOut());
803 if (!useHWDerivatives) {
804 // The gradient is order-1: Interpolate it across arccoord.zw.
805 // This doesn't work with perspective.
806 SkASSERT(!hasPerspective);
807 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
808 v->codeAppendf("%s.zw = derivatives * (%s.xy/radii * corner * 2);",
809 arcCoord.vsOut(), arcCoord.vsOut());
810 }
811 v->codeAppendf("} else {");
812 if (useHWDerivatives) {
813 v->codeAppendf("%s = float2(0);", arcCoord.vsOut());
814 } else {
815 v->codeAppendf("%s = float4(0);", arcCoord.vsOut());
816 }
817 v->codeAppendf("}");
818
819 // Emit the fragment shader.
820 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
821
822 f->codeAppendf("%s = half4(1);", args.fOutputCoverage);
823
824 // If x,y == 0, then we are drawing a triangle that does not track an arc.
825 f->codeAppendf("if (float2(0) != %s.xy) {", arcCoord.fsIn());
826 f->codeAppendf( "float fn = dot(%s.xy, %s.xy) - 1;", arcCoord.fsIn(), arcCoord.fsIn());
827 if (GrAAType::kMSAA == proc.fAAType) {
828 using ScopeFlags = GrGLSLFPFragmentBuilder::ScopeFlags;
829 if (!useHWDerivatives) {
830 f->codeAppendf("float2 grad = %s.zw;", arcCoord.fsIn());
831 f->applyFnToMultisampleMask("fn", "grad", ScopeFlags::kInsidePerPrimitiveBranch);
832 } else {
833 f->applyFnToMultisampleMask("fn", nullptr, ScopeFlags::kInsidePerPrimitiveBranch);
834 }
835 } else {
836 f->codeAppendf("if (fn > 0) {");
837 f->codeAppendf( "%s = half4(0);", args.fOutputCoverage);
838 f->codeAppendf("}");
839 }
840 f->codeAppendf("}");
841 }
842
Brian Osman609f1592020-07-01 15:14:39 -0400843 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&) override {}
Chris Dalton0dffbab2019-03-27 13:08:50 -0600844};
845
Robert Phillips366176b2020-02-26 11:40:50 -0500846GrGLSLPrimitiveProcessor* FillRRectOp::Processor::createGLSLInstance(
Chris Dalton133944a2018-11-16 23:30:29 -0500847 const GrShaderCaps&) const {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600848 if (GrAAType::kCoverage != fAAType) {
849 return new MSAAImpl();
850 }
851 return new CoverageImpl();
Chris Dalton133944a2018-11-16 23:30:29 -0500852}
853
Robert Phillipscad8fba2020-03-20 15:39:29 -0400854void FillRRectOp::onCreateProgramInfo(const GrCaps* caps,
855 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -0500856 const GrSurfaceProxyView& writeView,
Robert Phillipscad8fba2020-03-20 15:39:29 -0400857 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -0400858 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500859 GrXferBarrierFlags renderPassXferBarriers,
860 GrLoadOp colorLoadOp) {
Robert Phillips360ec182020-03-26 13:29:50 -0400861 GrGeometryProcessor* gp = Processor::Make(arena, fHelper.aaType(), fProcessorFlags);
Robert Phillipsce978572020-02-28 11:56:44 -0500862 SkASSERT(gp->instanceStride() == (size_t)fInstanceStride);
Chris Dalton133944a2018-11-16 23:30:29 -0500863
Brian Salomon8afde5f2020-04-01 16:22:00 -0400864 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -0400865 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -0500866 renderPassXferBarriers, colorLoadOp);
Robert Phillips8053c972019-11-21 10:44:53 -0500867}
868
Robert Phillips366176b2020-02-26 11:40:50 -0500869void FillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Robert Phillips8053c972019-11-21 10:44:53 -0500870 if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
871 return; // Setup failed.
872 }
873
874 if (!fProgramInfo) {
Robert Phillipscad8fba2020-03-20 15:39:29 -0400875 this->createProgramInfo(flushState);
Robert Phillips8053c972019-11-21 10:44:53 -0500876 }
Robert Phillips901aff02019-10-08 12:32:56 -0400877
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600878 flushState->bindPipelineAndScissorClip(*fProgramInfo, this->bounds());
879 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
Greg Daniel426274b2020-07-20 11:37:38 -0400880 flushState->bindBuffers(std::move(fIndexBuffer), std::move(fInstanceBuffer),
881 std::move(fVertexBuffer));
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600882 flushState->drawIndexedInstanced(fIndexCount, 0, fInstanceCount, fBaseInstance, 0);
Chris Dalton133944a2018-11-16 23:30:29 -0500883}
884
885// Will the given corner look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600886static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
Chris Dalton133944a2018-11-16 23:30:29 -0500887 Sk2f devRadii = devScale * cornerRadii;
888 if (devRadii[1] < devRadii[0]) {
889 devRadii = SkNx_shuffle<1,0>(devRadii);
890 }
Brian Osman788b9162020-02-07 10:36:46 -0500891 float minDevRadius = std::max(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
Chris Dalton133944a2018-11-16 23:30:29 -0500892 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
893 // This threshold was arrived at subjevtively on an NVIDIA chip.
894 return minDevRadius * minDevRadius * 5 > devRadii[1];
895}
896
Chris Dalton0dffbab2019-03-27 13:08:50 -0600897static bool can_use_hw_derivatives_with_coverage(
898 const Sk2f& devScale, const SkVector& cornerRadii) {
899 return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
Chris Dalton133944a2018-11-16 23:30:29 -0500900}
901
902// Will the given round rect look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600903static bool can_use_hw_derivatives_with_coverage(
904 const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
Chris Dalton133944a2018-11-16 23:30:29 -0500905 if (!shaderCaps.shaderDerivativeSupport()) {
906 return false;
907 }
908
909 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
910 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
911 Sk2f devScale = (x*x + y*y).sqrt();
912 switch (rrect.getType()) {
913 case SkRRect::kEmpty_Type:
914 case SkRRect::kRect_Type:
915 return true;
916
917 case SkRRect::kOval_Type:
918 case SkRRect::kSimple_Type:
Chris Dalton0dffbab2019-03-27 13:08:50 -0600919 return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
Chris Dalton133944a2018-11-16 23:30:29 -0500920
921 case SkRRect::kNinePatch_Type: {
922 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
923 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
924 Sk2f minRadii = Sk2f::Min(r0, r1);
925 Sk2f maxRadii = Sk2f::Max(r0, r1);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600926 return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
927 can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
Chris Dalton133944a2018-11-16 23:30:29 -0500928 }
929
930 case SkRRect::kComplex_Type: {
931 for (int i = 0; i < 4; ++i) {
932 auto corner = static_cast<SkRRect::Corner>(i);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600933 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
Chris Dalton133944a2018-11-16 23:30:29 -0500934 return false;
935 }
936 }
937 return true;
938 }
939 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600940 SK_ABORT("Invalid round rect type.");
Chris Dalton133944a2018-11-16 23:30:29 -0500941}
Robert Phillips366176b2020-02-26 11:40:50 -0500942
943} // anonymous namespace
944
945
Herb Derbyc76d4092020-10-07 16:46:15 -0400946GrOp::Owner GrFillRRectOp::Make(GrRecordingContext* ctx,
947 GrPaint&& paint,
948 const SkMatrix& viewMatrix,
949 const SkRRect& rrect,
950 GrAAType aaType) {
Robert Phillips360ec182020-03-26 13:29:50 -0400951 return FillRRectOp::Make(ctx, std::move(paint), viewMatrix, rrect, aaType);
Robert Phillips366176b2020-02-26 11:40:50 -0500952}
953
954#if GR_TEST_UTILS
955
956#include "src/gpu/GrDrawOpTest.h"
957
958GR_DRAW_OP_TEST_DEFINE(FillRRectOp) {
Robert Phillips366176b2020-02-26 11:40:50 -0500959 SkMatrix viewMatrix = GrTest::TestMatrix(random);
960 GrAAType aaType = GrAAType::kNone;
961 if (random->nextBool()) {
962 aaType = (numSamples > 1) ? GrAAType::kMSAA : GrAAType::kCoverage;
963 }
964
965 SkRect rect = GrTest::TestRect(random);
966 float w = rect.width();
967 float h = rect.height();
968
969 SkRRect rrect;
970 // TODO: test out other rrect configurations
971 rrect.setNinePatch(rect, w / 3.0f, h / 4.0f, w / 5.0f, h / 6.0);
972
973 return GrFillRRectOp::Make(context,
Robert Phillips360ec182020-03-26 13:29:50 -0400974 std::move(paint),
Robert Phillips366176b2020-02-26 11:40:50 -0500975 viewMatrix,
976 rrect,
Robert Phillips360ec182020-03-26 13:29:50 -0400977 aaType);
Robert Phillips366176b2020-02-26 11:40:50 -0500978}
979
980#endif