blob: a302099b15ab17efde18e1a4695257e2df05ff71 [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
Chris Dalton82eb9e72019-03-21 14:26:39 -06008#include "GrFillRRectOp.h"
Chris Dalton133944a2018-11-16 23:30:29 -05009
10#include "GrCaps.h"
Chris Dalton133944a2018-11-16 23:30:29 -050011#include "GrGpuCommandBuffer.h"
12#include "GrMemoryPool.h"
Robert Phillipsb97da532019-02-12 15:24:12 -050013#include "GrOpFlushState.h"
14#include "GrRecordingContext.h"
15#include "GrRecordingContextPriv.h"
Chris Dalton133944a2018-11-16 23:30:29 -050016#include "SkRRectPriv.h"
17#include "glsl/GrGLSLFragmentShaderBuilder.h"
18#include "glsl/GrGLSLGeometryProcessor.h"
19#include "glsl/GrGLSLVarying.h"
20#include "glsl/GrGLSLVertexGeoBuilder.h"
21
22// Hardware derivatives are not always accurate enough for highly elliptical corners. This method
23// checks to make sure the corners will still all look good if we use HW derivatives.
Chris Dalton0dffbab2019-03-27 13:08:50 -060024static bool can_use_hw_derivatives_with_coverage(
25 const GrShaderCaps&, const SkMatrix&, const SkRRect&);
Chris Dalton133944a2018-11-16 23:30:29 -050026
Chris Dalton82eb9e72019-03-21 14:26:39 -060027std::unique_ptr<GrFillRRectOp> GrFillRRectOp::Make(
Chris Dalton0dffbab2019-03-27 13:08:50 -060028 GrRecordingContext* ctx, GrAAType aaType, const SkMatrix& viewMatrix, const SkRRect& rrect,
Robert Phillipsb97da532019-02-12 15:24:12 -050029 const GrCaps& caps, GrPaint&& paint) {
Chris Dalton133944a2018-11-16 23:30:29 -050030 if (!caps.instanceAttribSupport()) {
31 return nullptr;
32 }
33
Chris Dalton0dffbab2019-03-27 13:08:50 -060034 Flags flags = Flags::kNone;
35 if (GrAAType::kCoverage == aaType) {
36 // TODO: Support perspective in a follow-on CL. This shouldn't be difficult, since we
37 // already use HW derivatives. The only trick will be adjusting the AA outset to account for
38 // perspective. (i.e., outset = 0.5 * z.)
39 if (viewMatrix.hasPerspective()) {
40 return nullptr;
41 }
42 if (can_use_hw_derivatives_with_coverage(*caps.shaderCaps(), viewMatrix, rrect)) {
43 // HW derivatives (more specifically, fwidth()) are consistently faster on all platforms
44 // in coverage mode. We use them as long as the approximation will be accurate enough.
45 flags |= Flags::kUseHWDerivatives;
46 }
47 } else {
48 if (GrAAType::kMSAA == aaType) {
49 if (!caps.sampleLocationsSupport() || !caps.shaderCaps()->sampleVariablesSupport()) {
50 return nullptr;
51 }
52 }
53 if (viewMatrix.hasPerspective()) {
54 // HW derivatives are consistently slower on all platforms in sample mask mode. We
55 // therefore only use them when there is perspective, since then we can't interpolate
56 // the symbolic screen-space gradient.
57 flags |= Flags::kUseHWDerivatives | Flags::kHasPerspective;
58 }
Chris Dalton133944a2018-11-16 23:30:29 -050059 }
60
61 // Produce a matrix that draws the round rect from normalized [-1, -1, +1, +1] space.
62 float l = rrect.rect().left(), r = rrect.rect().right(),
63 t = rrect.rect().top(), b = rrect.rect().bottom();
64 SkMatrix m;
65 // Unmap the normalized rect [-1, -1, +1, +1] back to [l, t, r, b].
66 m.setScaleTranslate((r - l)/2, (b - t)/2, (l + r)/2, (t + b)/2);
67 // Map to device space.
68 m.postConcat(viewMatrix);
69
Chris Dalton0dffbab2019-03-27 13:08:50 -060070 SkRect devBounds;
71 if (!(flags & Flags::kHasPerspective)) {
72 // Since m is an affine matrix that maps the rect [-1, -1, +1, +1] into the shape's
73 // device-space quad, it's quite simple to find the bounding rectangle:
74 devBounds = SkRect::MakeXYWH(m.getTranslateX(), m.getTranslateY(), 0, 0);
75 devBounds.outset(SkScalarAbs(m.getScaleX()) + SkScalarAbs(m.getSkewX()),
76 SkScalarAbs(m.getSkewY()) + SkScalarAbs(m.getScaleY()));
77 } else {
78 viewMatrix.mapRect(&devBounds, rrect.rect());
79 }
80
81 if (GrAAType::kMSAA == aaType && caps.preferTrianglesOverSampleMask()) {
82 // We are on a platform that prefers fine triangles instead of using the sample mask. See if
83 // the round rect is large enough that it will be faster for us to send it off to the
84 // default path renderer instead. The 200x200 threshold was arrived at using the
85 // "shapes_rrect" benchmark on an ARM Galaxy S9.
86 if (devBounds.height() * devBounds.width() > 200 * 200) {
87 return nullptr;
88 }
89 }
90
91 GrOpMemoryPool* pool = ctx->priv().opMemoryPool();
92 return pool->allocate<GrFillRRectOp>(aaType, rrect, flags, m, std::move(paint), devBounds);
93}
94
95GrFillRRectOp::GrFillRRectOp(
96 GrAAType aaType, const SkRRect& rrect, Flags flags,
97 const SkMatrix& totalShapeMatrix, GrPaint&& paint, const SkRect& devBounds)
98 : GrDrawOp(ClassID())
99 , fAAType(aaType)
100 , fOriginalColor(paint.getColor4f())
101 , fLocalRect(rrect.rect())
102 , fFlags(flags)
103 , fProcessors(std::move(paint)) {
104 SkASSERT((fFlags & Flags::kHasPerspective) == totalShapeMatrix.hasPerspective());
105 this->setBounds(devBounds, GrOp::HasAABloat::kYes, GrOp::IsZeroArea::kNo);
Chris Dalton133944a2018-11-16 23:30:29 -0500106
107 // Write the matrix attribs.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600108 const SkMatrix& m = totalShapeMatrix;
109 if (!(fFlags & Flags::kHasPerspective)) {
110 // Affine 2D transformation (float2x2 plus float2 translate).
111 SkASSERT(!m.hasPerspective());
112 this->writeInstanceData(m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY());
113 this->writeInstanceData(m.getTranslateX(), m.getTranslateY());
114 } else {
115 // Perspective float3x3 transformation matrix.
116 SkASSERT(m.hasPerspective());
117 m.get9(this->appendInstanceData<float>(9));
118 }
Chris Dalton133944a2018-11-16 23:30:29 -0500119
120 // Convert the radii to [-1, -1, +1, +1] space and write their attribs.
121 Sk4f radiiX, radiiY;
122 Sk4f::Load2(SkRRectPriv::GetRadiiArray(rrect), &radiiX, &radiiY);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600123 (radiiX * (2/rrect.width())).store(this->appendInstanceData<float>(4));
124 (radiiY * (2/rrect.height())).store(this->appendInstanceData<float>(4));
Chris Dalton133944a2018-11-16 23:30:29 -0500125
126 // We will write the color and local rect attribs during finalize().
127}
128
Chris Dalton82eb9e72019-03-21 14:26:39 -0600129GrProcessorSet::Analysis GrFillRRectOp::finalize(const GrCaps& caps, const GrAppliedClip* clip,
130 GrFSAAType fsaaType, GrClampType clampType) {
Chris Dalton133944a2018-11-16 23:30:29 -0500131 SkASSERT(1 == fInstanceCount);
132
133 SkPMColor4f overrideColor;
134 const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
Chris Daltonb8fff0d2019-03-05 10:11:58 -0700135
136 fOriginalColor, GrProcessorAnalysisCoverage::kSingleChannel, clip,
Brian Osman5ced0bf2019-03-15 10:15:29 -0400137 &GrUserStencilSettings::kUnused, fsaaType, caps, clampType, &overrideColor);
Chris Dalton133944a2018-11-16 23:30:29 -0500138
139 // Finish writing the instance attribs.
Brian Osman5105d682019-02-13 16:06:14 -0500140 SkPMColor4f finalColor = analysis.inputColorIsOverridden() ? overrideColor : fOriginalColor;
141 if (!SkPMColor4fFitsInBytes(finalColor)) {
142 fFlags |= Flags::kWideColor;
143 uint32_t halfColor[2];
144 SkFloatToHalf_finite_ftz(Sk4f::Load(finalColor.vec())).store(&halfColor);
145 this->writeInstanceData(halfColor[0], halfColor[1]);
146 } else {
147 this->writeInstanceData(finalColor.toBytes_RGBA());
148 }
149
Chris Dalton133944a2018-11-16 23:30:29 -0500150 if (analysis.usesLocalCoords()) {
151 this->writeInstanceData(fLocalRect);
152 fFlags |= Flags::kHasLocalCoords;
153 }
154 fInstanceStride = fInstanceData.count();
155
Chris Dalton4b62aed2019-01-15 11:53:00 -0700156 return analysis;
Chris Dalton133944a2018-11-16 23:30:29 -0500157}
158
Chris Dalton82eb9e72019-03-21 14:26:39 -0600159GrDrawOp::CombineResult GrFillRRectOp::onCombineIfPossible(GrOp* op, const GrCaps&) {
160 const auto& that = *op->cast<GrFillRRectOp>();
Chris Dalton133944a2018-11-16 23:30:29 -0500161 if (fFlags != that.fFlags || fProcessors != that.fProcessors ||
162 fInstanceData.count() > std::numeric_limits<int>::max() - that.fInstanceData.count()) {
163 return CombineResult::kCannotCombine;
164 }
165
166 fInstanceData.push_back_n(that.fInstanceData.count(), that.fInstanceData.begin());
167 fInstanceCount += that.fInstanceCount;
168 SkASSERT(fInstanceStride == that.fInstanceStride);
169 return CombineResult::kMerged;
170}
171
Chris Dalton82eb9e72019-03-21 14:26:39 -0600172void GrFillRRectOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton133944a2018-11-16 23:30:29 -0500173 if (void* instanceData = flushState->makeVertexSpace(fInstanceStride, fInstanceCount,
174 &fInstanceBuffer, &fBaseInstance)) {
175 SkASSERT(fInstanceStride * fInstanceCount == fInstanceData.count());
176 memcpy(instanceData, fInstanceData.begin(), fInstanceData.count());
177 }
178}
179
Chris Dalton0dffbab2019-03-27 13:08:50 -0600180class GrFillRRectOp::Processor : public GrGeometryProcessor {
181public:
182 Processor(GrAAType aaType, Flags flags)
183 : GrGeometryProcessor(kGrFillRRectOp_Processor_ClassID)
184 , fAAType(aaType)
185 , fFlags(flags) {
186 int numVertexAttribs = (GrAAType::kCoverage == fAAType) ? 3 : 2;
187 this->setVertexAttributes(kVertexAttribs, numVertexAttribs);
Chris Dalton133944a2018-11-16 23:30:29 -0500188
Chris Dalton0dffbab2019-03-27 13:08:50 -0600189 if (!(flags & Flags::kHasPerspective)) {
190 // Affine 2D transformation (float2x2 plus float2 translate).
191 fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
192 fInstanceAttribs.emplace_back(
193 "translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
194 } else {
195 // Perspective float3x3 transformation matrix.
196 fInstanceAttribs.emplace_back("persp_x", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
197 fInstanceAttribs.emplace_back("persp_y", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
198 fInstanceAttribs.emplace_back("persp_z", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
199 }
200 fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
201 fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
202 fColorAttrib = &fInstanceAttribs.push_back(
203 MakeColorAttribute("color", (flags & Flags::kWideColor)));
204 if (fFlags & Flags::kHasLocalCoords) {
205 fInstanceAttribs.emplace_back(
206 "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
207 }
208 this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
209
210 if (GrAAType::kMSAA == fAAType) {
211 this->setWillUseCustomFeature(CustomFeatures::kSampleLocations);
212 }
213 }
214
215 const char* name() const override { return "GrFillRRectOp::Processor"; }
216
217 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
218 b->add32(((uint32_t)fFlags << 16) | (uint32_t)fAAType);
219 }
220
221 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
222
223private:
224 static constexpr Attribute kVertexAttribs[] = {
225 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
226 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
227 // Coverage only.
228 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
229
230 const GrAAType fAAType;
231 const Flags fFlags;
232
233 SkSTArray<6, Attribute> fInstanceAttribs;
234 const Attribute* fColorAttrib;
235
236 class CoverageImpl;
237 class MSAAImpl;
238};
239
240constexpr GrPrimitiveProcessor::Attribute GrFillRRectOp::Processor::kVertexAttribs[];
241
242// Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
Chris Dalton133944a2018-11-16 23:30:29 -0500243// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
244// edges. The Vertex struct tells the shader where to place its vertex within a normalized
245// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600246struct CoverageVertex {
Chris Dalton133944a2018-11-16 23:30:29 -0500247 std::array<float, 4> fRadiiSelector;
248 std::array<float, 2> fCorner;
249 std::array<float, 2> fRadiusOutset;
250 std::array<float, 2> fAABloatDirection;
251 float fCoverage;
252 float fIsLinearCoverage;
Chris Dalton133944a2018-11-16 23:30:29 -0500253};
254
255// This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
256// of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
257// rectangles.
258static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
259
Chris Dalton0dffbab2019-03-27 13:08:50 -0600260static constexpr CoverageVertex kCoverageVertexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500261 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700262 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
263 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500264
265 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700266 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
267 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500268
269 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700270 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
271 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500272
273 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700274 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
275 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500276
277
278 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700279 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
280 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500281
282 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700283 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
284 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500285
286 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700287 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
288 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500289
290 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700291 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
292 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500293
294
295 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700296 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
297 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
298 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
299 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
300 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
301 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500302
303 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700304 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
305 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
306 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
307 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
308 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
309 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500310
311 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700312 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
313 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
314 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
315 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
316 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
317 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500318
319 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700320 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
321 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
322 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
323 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
Chris Dalton2d07e862018-11-26 12:30:47 -0700324 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700325 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500326
Chris Dalton0dffbab2019-03-27 13:08:50 -0600327GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500328
Chris Dalton0dffbab2019-03-27 13:08:50 -0600329static constexpr uint16_t kCoverageIndexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500330 // Inset octagon (solid coverage).
331 0, 1, 7,
332 1, 2, 7,
333 7, 2, 6,
334 2, 3, 6,
335 6, 3, 5,
336 3, 4, 5,
337
338 // AA borders (linear coverage).
339 0, 1, 8, 1, 9, 8,
340 2, 3, 10, 3, 11, 10,
341 4, 5, 12, 5, 13, 12,
342 6, 7, 14, 7, 15, 14,
343
344 // Top-left arc.
345 16, 17, 21,
346 17, 21, 18,
347 21, 18, 20,
348 18, 20, 19,
349
350 // Top-right arc.
351 22, 23, 27,
352 23, 27, 24,
353 27, 24, 26,
354 24, 26, 25,
355
356 // Bottom-right arc.
357 28, 29, 33,
358 29, 33, 30,
359 33, 30, 32,
360 30, 32, 31,
361
362 // Bottom-left arc.
363 34, 35, 39,
364 35, 39, 36,
365 39, 36, 38,
366 36, 38, 37};
367
Chris Dalton0dffbab2019-03-27 13:08:50 -0600368GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500369
Chris Dalton0dffbab2019-03-27 13:08:50 -0600370class GrFillRRectOp::Processor::CoverageImpl : public GrGLSLGeometryProcessor {
Chris Dalton133944a2018-11-16 23:30:29 -0500371 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
372 const auto& proc = args.fGP.cast<Processor>();
373 bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives);
374
Chris Dalton0dffbab2019-03-27 13:08:50 -0600375 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
376
Chris Dalton133944a2018-11-16 23:30:29 -0500377 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
378 varyings->emitAttributes(proc);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600379 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500380 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
381
382 // Emit the vertex shader.
383 GrGLSLVertexBuilder* v = args.fVertBuilder;
384
385 // Unpack vertex attribs.
386 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
387 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
388 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
389 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
390 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
391
392 // Find the amount to bloat each edge for AA (in source space).
393 v->codeAppend("float2 pixellength = inversesqrt("
394 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
395 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
396 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
397 "abs(normalized_axis_dirs.zw));");
398 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
399
400 // Identify our radii.
Mike Reedd3efa992018-11-28 13:13:15 +0000401 v->codeAppend("float4 radii_and_neighbors = radii_selector"
402 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
403 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
404 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500405
406 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
407 // The rrect is more narrow than an AA coverage ramp. We can't draw as-is
408 // or else opposite AA borders will overlap. Instead, fudge the size up to
409 // the width of a coverage ramp, and then reduce total coverage to make
410 // the rect appear more thin.
411 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
412 v->codeAppend( "coverage /= max(aa_bloatradius.x, 1) * max(aa_bloatradius.y, 1);");
413 // Set radii to zero to ensure we take the "linear coverage" codepath.
414 // (The "coverage" variable only has effect in the linear codepath.)
415 v->codeAppend( "radii = float2(0);");
416 v->codeAppend("}");
417
418 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.25))) {");
419 // The radii are very small. Demote this arc to a sharp 90 degree corner.
420 v->codeAppend( "radii = aa_bloatradius;");
421 // Snap octagon vertices to the corner of the bounding box.
422 v->codeAppend( "radius_outset = floor(abs(radius_outset)) * radius_outset;");
423 v->codeAppend( "is_linear_coverage = 1;");
424 v->codeAppend("} else {");
Mike Reedd3efa992018-11-28 13:13:15 +0000425 // Don't let radii get smaller than a pixel.
Chris Dalton133944a2018-11-16 23:30:29 -0500426 v->codeAppend( "radii = clamp(radii, pixellength, 2 - pixellength);");
Mike Reedd3efa992018-11-28 13:13:15 +0000427 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength, 2 - pixellength);");
428 // Don't let neighboring radii get closer together than 1/16 pixel.
429 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
430 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
431 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500432 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500433
434 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
435 // normalized [-1,-1,+1,+1] space.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700436 v->codeAppend("float2 aa_outset = aa_bloat_direction.xy * aa_bloatradius;");
437 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500438
439 // Emit transforms.
440 GrShaderVar localCoord("", kFloat2_GrSLType);
441 if (proc.fFlags & Flags::kHasLocalCoords) {
442 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
443 "local_rect.zw * (1 + vertexpos)) * .5;");
444 localCoord.set(kFloat2_GrSLType, "localcoord");
445 }
446 this->emitTransforms(v, varyings, args.fUniformHandler, localCoord,
447 args.fFPCoordTransformHandler);
448
449 // Transform to device space.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600450 SkASSERT(!(proc.fFlags & Flags::kHasPerspective));
Chris Dalton133944a2018-11-16 23:30:29 -0500451 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
452 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
453 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
454
455 // Setup interpolants for coverage.
456 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
457 varyings->addVarying("arccoord", &arcCoord);
458 v->codeAppend("if (0 != is_linear_coverage) {");
459 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
460 // interpolate linear coverage across y.
461 v->codeAppendf( "%s.xy = float2(0, coverage);", arcCoord.vsOut());
462 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700463 // Find the normalized arc coordinates for our corner ellipse.
464 // (i.e., the coordinate system where x^2 + y^2 == 1).
465 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500466 // We are a corner piece: Interpolate the arc coordinates for coverage.
467 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
468 // instructs the fragment shader to use linear coverage).
469 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
470 if (!useHWDerivatives) {
471 // The gradient is order-1: Interpolate it across arccoord.zw.
472 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
473 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
474 }
475 v->codeAppend("}");
476
477 // Emit the fragment shader.
478 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
479
480 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
481 f->codeAppendf("half coverage;");
482 f->codeAppendf("if (0 == x_plus_1) {");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600483 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500484 f->codeAppendf("} else {");
485 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
486 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
487 if (useHWDerivatives) {
488 f->codeAppendf("float fnwidth = fwidth(fn);");
489 } else {
490 // The gradient is interpolated across arccoord.zw.
491 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
492 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
493 }
Ethan Nicholase1f55022019-02-05 17:17:40 -0500494 f->codeAppendf( "half d = half(fn/fnwidth);");
Chris Dalton133944a2018-11-16 23:30:29 -0500495 f->codeAppendf( "coverage = clamp(.5 - d, 0, 1);");
496 f->codeAppendf("}");
497 f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage);
498 }
499
500 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
501 FPCoordTransformIter&& transformIter) override {
502 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
503 }
504};
505
Chris Dalton0dffbab2019-03-27 13:08:50 -0600506// Our MSAA geometry consists of an inset octagon with full sample mask coverage, circumscribed
507// by a larger octagon that modifies the sample mask for the arc at each corresponding corner.
508struct MSAAVertex {
509 std::array<float, 4> fRadiiSelector;
510 std::array<float, 2> fCorner;
511 std::array<float, 2> fRadiusOutset;
512};
513
514static constexpr MSAAVertex kMSAAVertexData[] = {
515 // Left edge. (Negative radii selector indicates this is not an arc section.)
516 {{{0,0,0,-1}}, {{-1,+1}}, {{0,-1}}},
517 {{{-1,0,0,0}}, {{-1,-1}}, {{0,+1}}},
518
519 // Top edge.
520 {{{-1,0,0,0}}, {{-1,-1}}, {{+1,0}}},
521 {{{0,-1,0,0}}, {{+1,-1}}, {{-1,0}}},
522
523 // Right edge.
524 {{{0,-1,0,0}}, {{+1,-1}}, {{0,+1}}},
525 {{{0,0,-1,0}}, {{+1,+1}}, {{0,-1}}},
526
527 // Bottom edge.
528 {{{0,0,-1,0}}, {{+1,+1}}, {{-1,0}}},
529 {{{0,0,0,-1}}, {{-1,+1}}, {{+1,0}}},
530
531 // Top-left corner.
532 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}},
533 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}},
534 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}},
535 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}},
536
537 // Top-right corner.
538 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}},
539 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}},
540 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}},
541 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}},
542
543 // Bottom-right corner.
544 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}},
545 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}},
546 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}},
547 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}},
548
549 // Bottom-left corner.
550 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}},
551 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}},
552 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}},
553 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}}};
554
555GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
556
557static constexpr uint16_t kMSAAIndexData[] = {
558 // Inset octagon. (Full sample mask.)
559 0, 1, 2,
560 0, 2, 3,
561 0, 3, 6,
562 3, 4, 5,
563 3, 5, 6,
564 6, 7, 0,
565
566 // Top-left arc. (Sample mask is set to the arc.)
567 8, 9, 10,
568 9, 11, 10,
569
570 // Top-right arc.
571 12, 13, 14,
572 13, 15, 14,
573
574 // Bottom-right arc.
575 16, 17, 18,
576 17, 19, 18,
577
578 // Bottom-left arc.
579 20, 21, 22,
580 21, 23, 22};
581
582GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
583
584class GrFillRRectOp::Processor::MSAAImpl : public GrGLSLGeometryProcessor {
585 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
586 const auto& proc = args.fGP.cast<Processor>();
587 bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives);
588 bool hasPerspective = (proc.fFlags & Flags::kHasPerspective);
589 bool hasLocalCoords = (proc.fFlags & Flags::kHasLocalCoords);
590 SkASSERT(useHWDerivatives == hasPerspective);
591
592 SkASSERT(proc.vertexStride() == sizeof(MSAAVertex));
593
594 // Emit the vertex shader.
595 GrGLSLVertexBuilder* v = args.fVertBuilder;
596
597 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
598 varyings->emitAttributes(proc);
599 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
600 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
601
602 // Unpack vertex attribs.
603 v->codeAppendf("float2 corner = corner_and_radius_outsets.xy;");
604 v->codeAppendf("float2 radius_outset = corner_and_radius_outsets.zw;");
605
606 // Identify our radii.
607 v->codeAppend("float2 radii;");
608 v->codeAppend("radii.x = dot(radii_selector, radii_x);");
609 v->codeAppend("radii.y = dot(radii_selector, radii_y);");
610 v->codeAppendf("bool is_arc_section = (radii.x > 0);");
611 v->codeAppendf("radii = abs(radii);");
612
613 // Find our vertex position, adjusted for radii. Our rect is drawn in normalized
614 // [-1,-1,+1,+1] space.
615 v->codeAppend("float2 vertexpos = corner + radius_outset * radii;");
616
617 // Emit transforms.
618 GrShaderVar localCoord("", kFloat2_GrSLType);
619 if (hasLocalCoords) {
620 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
621 "local_rect.zw * (1 + vertexpos)) * .5;");
622 localCoord.set(kFloat2_GrSLType, "localcoord");
623 }
624 this->emitTransforms(v, varyings, args.fUniformHandler, localCoord,
625 args.fFPCoordTransformHandler);
626
627 // Transform to device space.
628 if (!hasPerspective) {
629 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
630 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
631 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
632 } else {
633 v->codeAppend("float3x3 persp_matrix = float3x3(persp_x, persp_y, persp_z);");
634 v->codeAppend("float3 devcoord = float3(vertexpos, 1) * persp_matrix;");
635 gpArgs->fPositionVar.set(kFloat3_GrSLType, "devcoord");
636 }
637
638 // Determine normalized arc coordinates for the implicit function.
639 GrGLSLVarying arcCoord((useHWDerivatives) ? kFloat2_GrSLType : kFloat4_GrSLType);
640 varyings->addVarying("arccoord", &arcCoord);
641 v->codeAppendf("if (is_arc_section) {");
642 v->codeAppendf( "%s.xy = 1 - abs(radius_outset);", arcCoord.vsOut());
643 if (!useHWDerivatives) {
644 // The gradient is order-1: Interpolate it across arccoord.zw.
645 // This doesn't work with perspective.
646 SkASSERT(!hasPerspective);
647 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
648 v->codeAppendf("%s.zw = derivatives * (%s.xy/radii * corner * 2);",
649 arcCoord.vsOut(), arcCoord.vsOut());
650 }
651 v->codeAppendf("} else {");
652 if (useHWDerivatives) {
653 v->codeAppendf("%s = float2(0);", arcCoord.vsOut());
654 } else {
655 v->codeAppendf("%s = float4(0);", arcCoord.vsOut());
656 }
657 v->codeAppendf("}");
658
659 // Emit the fragment shader.
660 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
661
662 f->codeAppendf("%s = half4(1);", args.fOutputCoverage);
663
664 // If x,y == 0, then we are drawing a triangle that does not track an arc.
665 f->codeAppendf("if (float2(0) != %s.xy) {", arcCoord.fsIn());
666 f->codeAppendf( "float fn = dot(%s.xy, %s.xy) - 1;", arcCoord.fsIn(), arcCoord.fsIn());
667 if (GrAAType::kMSAA == proc.fAAType) {
668 using ScopeFlags = GrGLSLFPFragmentBuilder::ScopeFlags;
669 if (!useHWDerivatives) {
670 f->codeAppendf("float2 grad = %s.zw;", arcCoord.fsIn());
671 f->applyFnToMultisampleMask("fn", "grad", ScopeFlags::kInsidePerPrimitiveBranch);
672 } else {
673 f->applyFnToMultisampleMask("fn", nullptr, ScopeFlags::kInsidePerPrimitiveBranch);
674 }
675 } else {
676 f->codeAppendf("if (fn > 0) {");
677 f->codeAppendf( "%s = half4(0);", args.fOutputCoverage);
678 f->codeAppendf("}");
679 }
680 f->codeAppendf("}");
681 }
682
683 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
684 FPCoordTransformIter&& transformIter) override {
685 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
686 }
687};
688
Chris Dalton82eb9e72019-03-21 14:26:39 -0600689GrGLSLPrimitiveProcessor* GrFillRRectOp::Processor::createGLSLInstance(
Chris Dalton133944a2018-11-16 23:30:29 -0500690 const GrShaderCaps&) const {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600691 if (GrAAType::kCoverage != fAAType) {
692 return new MSAAImpl();
693 }
694 return new CoverageImpl();
Chris Dalton133944a2018-11-16 23:30:29 -0500695}
696
Chris Dalton82eb9e72019-03-21 14:26:39 -0600697void GrFillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Dalton133944a2018-11-16 23:30:29 -0500698 if (!fInstanceBuffer) {
699 return; // Setup failed.
700 }
701
Chris Dalton0dffbab2019-03-27 13:08:50 -0600702 sk_sp<const GrBuffer> indexBuffer, vertexBuffer;
703 int indexCount;
Chris Dalton133944a2018-11-16 23:30:29 -0500704
Chris Dalton0dffbab2019-03-27 13:08:50 -0600705 if (GrAAType::kCoverage == fAAType) {
706 GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
707
708 indexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
709 GrGpuBufferType::kIndex, sizeof(kCoverageIndexData), kCoverageIndexData,
710 gCoverageIndexBufferKey);
711
712 GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
713
714 vertexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
715 GrGpuBufferType::kVertex, sizeof(kCoverageVertexData), kCoverageVertexData,
716 gCoverageVertexBufferKey);
717
718 indexCount = SK_ARRAY_COUNT(kCoverageIndexData);
719 } else {
720 GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
721
722 indexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
723 GrGpuBufferType::kIndex, sizeof(kMSAAIndexData), kMSAAIndexData,
724 gMSAAIndexBufferKey);
725
726 GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
727
728 vertexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
729 GrGpuBufferType::kVertex, sizeof(kMSAAVertexData), kMSAAVertexData,
730 gMSAAVertexBufferKey);
731
732 indexCount = SK_ARRAY_COUNT(kMSAAIndexData);
733 }
734
735 if (!indexBuffer || !vertexBuffer) {
Chris Dalton133944a2018-11-16 23:30:29 -0500736 return;
737 }
738
Chris Dalton0dffbab2019-03-27 13:08:50 -0600739 Processor proc(fAAType, fFlags);
Chris Dalton133944a2018-11-16 23:30:29 -0500740 SkASSERT(proc.instanceStride() == (size_t)fInstanceStride);
741
742 GrPipeline::InitArgs initArgs;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600743 if (GrAAType::kMSAA == fAAType) {
Chris Daltonbaa1b352019-04-03 12:03:00 -0600744 initArgs.fInputFlags = GrPipeline::InputFlags::kHWAntialias;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600745 }
Chris Dalton133944a2018-11-16 23:30:29 -0500746 initArgs.fCaps = &flushState->caps();
747 initArgs.fResourceProvider = flushState->resourceProvider();
748 initArgs.fDstProxy = flushState->drawOpArgs().fDstProxy;
Chris Dalton8fa16252018-11-19 13:37:31 -0700749 auto clip = flushState->detachAppliedClip();
750 GrPipeline::FixedDynamicState fixedDynamicState(clip.scissorState().rect());
751 GrPipeline pipeline(initArgs, std::move(fProcessors), std::move(clip));
Chris Dalton133944a2018-11-16 23:30:29 -0500752
753 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600754 mesh.setIndexedInstanced(
755 std::move(indexBuffer), indexCount, fInstanceBuffer, fInstanceCount, fBaseInstance,
756 GrPrimitiveRestart::kNo);
Brian Salomon12d22642019-01-29 14:38:50 -0500757 mesh.setVertexData(std::move(vertexBuffer));
Chris Dalton0dffbab2019-03-27 13:08:50 -0600758 flushState->rtCommandBuffer()->draw(
759 proc, pipeline, &fixedDynamicState, nullptr, &mesh, 1, this->bounds());
Chris Dalton133944a2018-11-16 23:30:29 -0500760}
761
762// Will the given corner look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600763static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
Chris Dalton133944a2018-11-16 23:30:29 -0500764 Sk2f devRadii = devScale * cornerRadii;
765 if (devRadii[1] < devRadii[0]) {
766 devRadii = SkNx_shuffle<1,0>(devRadii);
767 }
768 float minDevRadius = SkTMax(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
769 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
770 // This threshold was arrived at subjevtively on an NVIDIA chip.
771 return minDevRadius * minDevRadius * 5 > devRadii[1];
772}
773
Chris Dalton0dffbab2019-03-27 13:08:50 -0600774static bool can_use_hw_derivatives_with_coverage(
775 const Sk2f& devScale, const SkVector& cornerRadii) {
776 return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
Chris Dalton133944a2018-11-16 23:30:29 -0500777}
778
779// Will the given round rect look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600780static bool can_use_hw_derivatives_with_coverage(
781 const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
Chris Dalton133944a2018-11-16 23:30:29 -0500782 if (!shaderCaps.shaderDerivativeSupport()) {
783 return false;
784 }
785
786 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
787 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
788 Sk2f devScale = (x*x + y*y).sqrt();
789 switch (rrect.getType()) {
790 case SkRRect::kEmpty_Type:
791 case SkRRect::kRect_Type:
792 return true;
793
794 case SkRRect::kOval_Type:
795 case SkRRect::kSimple_Type:
Chris Dalton0dffbab2019-03-27 13:08:50 -0600796 return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
Chris Dalton133944a2018-11-16 23:30:29 -0500797
798 case SkRRect::kNinePatch_Type: {
799 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
800 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
801 Sk2f minRadii = Sk2f::Min(r0, r1);
802 Sk2f maxRadii = Sk2f::Max(r0, r1);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600803 return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
804 can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
Chris Dalton133944a2018-11-16 23:30:29 -0500805 }
806
807 case SkRRect::kComplex_Type: {
808 for (int i = 0; i < 4; ++i) {
809 auto corner = static_cast<SkRRect::Corner>(i);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600810 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
Chris Dalton133944a2018-11-16 23:30:29 -0500811 return false;
812 }
813 }
814 return true;
815 }
816 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600817 SK_ABORT("Invalid round rect type.");
Chris Dalton133944a2018-11-16 23:30:29 -0500818 return false; // Add this return to keep GCC happy.
819}