1 /*
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
8 #include "GrAAFillRRectOp.h"
9
10 #include "GrCaps.h"
11 #include "GrContextPriv.h"
12 #include "GrGpuCommandBuffer.h"
13 #include "GrMemoryPool.h"
14 #include "SkRRectPriv.h"
15 #include "glsl/GrGLSLFragmentShaderBuilder.h"
16 #include "glsl/GrGLSLGeometryProcessor.h"
17 #include "glsl/GrGLSLVarying.h"
18 #include "glsl/GrGLSLVertexGeoBuilder.h"
19
20 // Hardware derivatives are not always accurate enough for highly elliptical corners. This method
21 // checks to make sure the corners will still all look good if we use HW derivatives.
22 static bool can_use_hw_derivatives(const GrShaderCaps&, const SkMatrix&, const SkRRect&);
23
Make(GrContext * ctx,const SkMatrix & viewMatrix,const SkRRect & rrect,const GrCaps & caps,GrPaint && paint)24 std::unique_ptr<GrAAFillRRectOp> GrAAFillRRectOp::Make(
25 GrContext* ctx, const SkMatrix& viewMatrix, const SkRRect& rrect, const GrCaps& caps,
26 GrPaint&& paint) {
27 if (!caps.instanceAttribSupport()) {
28 return nullptr;
29 }
30
31 // TODO: Support perspective in a follow-on CL. This shouldn't be difficult, since we already
32 // use HW derivatives. The only trick will be adjusting the AA outset to account for
33 // perspective. (i.e., outset = 0.5 * z.)
34 if (viewMatrix.hasPerspective()) {
35 return nullptr;
36 }
37
38 GrOpMemoryPool* pool = ctx->contextPriv().opMemoryPool();
39 return pool->allocate<GrAAFillRRectOp>(*caps.shaderCaps(), viewMatrix, rrect, std::move(paint));
40 }
41
GrAAFillRRectOp(const GrShaderCaps & shaderCaps,const SkMatrix & viewMatrix,const SkRRect & rrect,GrPaint && paint)42 GrAAFillRRectOp::GrAAFillRRectOp(const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
43 const SkRRect& rrect, GrPaint&& paint)
44 : GrDrawOp(ClassID())
45 , fOriginalColor(paint.getColor4f())
46 , fLocalRect(rrect.rect())
47 , fProcessors(std::move(paint)) {
48 if (can_use_hw_derivatives(shaderCaps, viewMatrix, rrect)) {
49 fFlags |= Flags::kUseHWDerivatives;
50 }
51
52 // Produce a matrix that draws the round rect from normalized [-1, -1, +1, +1] space.
53 float l = rrect.rect().left(), r = rrect.rect().right(),
54 t = rrect.rect().top(), b = rrect.rect().bottom();
55 SkMatrix m;
56 // Unmap the normalized rect [-1, -1, +1, +1] back to [l, t, r, b].
57 m.setScaleTranslate((r - l)/2, (b - t)/2, (l + r)/2, (t + b)/2);
58 // Map to device space.
59 m.postConcat(viewMatrix);
60
61 // Since m is an affine matrix that maps the rect [-1, -1, +1, +1] into the shape's
62 // device-space quad, it's quite simple to find the bounding rectangle:
63 SkASSERT(!m.hasPerspective());
64 SkRect bounds = SkRect::MakeXYWH(m.getTranslateX(), m.getTranslateY(), 0, 0);
65 bounds.outset(SkScalarAbs(m.getScaleX()) + SkScalarAbs(m.getSkewX()),
66 SkScalarAbs(m.getSkewY()) + SkScalarAbs(m.getScaleY()));
67 this->setBounds(bounds, GrOp::HasAABloat::kYes, GrOp::IsZeroArea::kNo);
68
69 // Write the matrix attribs.
70 this->writeInstanceData(m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY());
71 this->writeInstanceData(m.getTranslateX(), m.getTranslateY());
72
73 // Convert the radii to [-1, -1, +1, +1] space and write their attribs.
74 Sk4f radiiX, radiiY;
75 Sk4f::Load2(SkRRectPriv::GetRadiiArray(rrect), &radiiX, &radiiY);
76 (radiiX * (2/(r - l))).store(this->appendInstanceData<float>(4));
77 (radiiY * (2/(b - t))).store(this->appendInstanceData<float>(4));
78
79 // We will write the color and local rect attribs during finalize().
80 }
81
finalize(const GrCaps & caps,const GrAppliedClip * clip)82 GrProcessorSet::Analysis GrAAFillRRectOp::finalize(const GrCaps& caps, const GrAppliedClip* clip) {
83 SkASSERT(1 == fInstanceCount);
84
85 SkPMColor4f overrideColor;
86 const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
87 fOriginalColor, GrProcessorAnalysisCoverage::kSingleChannel, clip, false, caps,
88 &overrideColor);
89
90 // Finish writing the instance attribs.
91 this->writeInstanceData(
92 (analysis.inputColorIsOverridden() ? overrideColor : fOriginalColor).toBytes_RGBA());
93 if (analysis.usesLocalCoords()) {
94 this->writeInstanceData(fLocalRect);
95 fFlags |= Flags::kHasLocalCoords;
96 }
97 fInstanceStride = fInstanceData.count();
98
99 return analysis;
100 }
101
onCombineIfPossible(GrOp * op,const GrCaps &)102 GrDrawOp::CombineResult GrAAFillRRectOp::onCombineIfPossible(GrOp* op, const GrCaps&) {
103 const auto& that = *op->cast<GrAAFillRRectOp>();
104 if (fFlags != that.fFlags || fProcessors != that.fProcessors ||
105 fInstanceData.count() > std::numeric_limits<int>::max() - that.fInstanceData.count()) {
106 return CombineResult::kCannotCombine;
107 }
108
109 fInstanceData.push_back_n(that.fInstanceData.count(), that.fInstanceData.begin());
110 fInstanceCount += that.fInstanceCount;
111 SkASSERT(fInstanceStride == that.fInstanceStride);
112 return CombineResult::kMerged;
113 }
114
onPrepare(GrOpFlushState * flushState)115 void GrAAFillRRectOp::onPrepare(GrOpFlushState* flushState) {
116 if (void* instanceData = flushState->makeVertexSpace(fInstanceStride, fInstanceCount,
117 &fInstanceBuffer, &fBaseInstance)) {
118 SkASSERT(fInstanceStride * fInstanceCount == fInstanceData.count());
119 memcpy(instanceData, fInstanceData.begin(), fInstanceData.count());
120 }
121 }
122
123 namespace {
124
125 // Our round rect geometry consists of an inset octagon with solid coverage, surrounded by linear
126 // coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
127 // edges. The Vertex struct tells the shader where to place its vertex within a normalized
128 // ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
129 struct Vertex {
130 std::array<float, 4> fRadiiSelector;
131 std::array<float, 2> fCorner;
132 std::array<float, 2> fRadiusOutset;
133 std::array<float, 2> fAABloatDirection;
134 float fCoverage;
135 float fIsLinearCoverage;
136 };
137
138 // This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
139 // of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
140 // rectangles.
141 static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
142
143 static constexpr Vertex kVertexData[] = {
144 // Left inset edge.
145 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
146 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
147
148 // Top inset edge.
149 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
150 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
151
152 // Right inset edge.
153 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
154 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
155
156 // Bottom inset edge.
157 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
158 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
159
160
161 // Left outset edge.
162 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
163 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
164
165 // Top outset edge.
166 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
167 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
168
169 // Right outset edge.
170 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
171 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
172
173 // Bottom outset edge.
174 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
175 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
176
177
178 // Top-left corner.
179 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
180 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
181 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
182 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
183 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
184 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
185
186 // Top-right corner.
187 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
188 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
189 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
190 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
191 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
192 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
193
194 // Bottom-right corner.
195 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
196 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
197 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
198 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
199 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
200 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
201
202 // Bottom-left corner.
203 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
204 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
205 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
206 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
207 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
208 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
209
210 GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);
211
212 static constexpr uint16_t kIndexData[] = {
213 // Inset octagon (solid coverage).
214 0, 1, 7,
215 1, 2, 7,
216 7, 2, 6,
217 2, 3, 6,
218 6, 3, 5,
219 3, 4, 5,
220
221 // AA borders (linear coverage).
222 0, 1, 8, 1, 9, 8,
223 2, 3, 10, 3, 11, 10,
224 4, 5, 12, 5, 13, 12,
225 6, 7, 14, 7, 15, 14,
226
227 // Top-left arc.
228 16, 17, 21,
229 17, 21, 18,
230 21, 18, 20,
231 18, 20, 19,
232
233 // Top-right arc.
234 22, 23, 27,
235 23, 27, 24,
236 27, 24, 26,
237 24, 26, 25,
238
239 // Bottom-right arc.
240 28, 29, 33,
241 29, 33, 30,
242 33, 30, 32,
243 30, 32, 31,
244
245 // Bottom-left arc.
246 34, 35, 39,
247 35, 39, 36,
248 39, 36, 38,
249 36, 38, 37};
250
251 GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
252
253 }
254
255 class GrAAFillRRectOp::Processor : public GrGeometryProcessor {
256 public:
Processor(Flags flags)257 Processor(Flags flags)
258 : GrGeometryProcessor(kGrAAFillRRectOp_Processor_ClassID)
259 , fFlags(flags) {
260 this->setVertexAttributes(kVertexAttribs, 3);
261 this->setInstanceAttributes(kInstanceAttribs, (flags & Flags::kHasLocalCoords) ? 6 : 5);
262 SkASSERT(this->vertexStride() == sizeof(Vertex));
263 }
264
name() const265 const char* name() const override { return "GrAAFillRRectOp::Processor"; }
266
getGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const267 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
268 b->add32(static_cast<uint32_t>(fFlags));
269 }
270
271 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
272
273 private:
274 static constexpr Attribute kVertexAttribs[] = {
275 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
276 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
277 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
278
279 static constexpr Attribute kInstanceAttribs[] = {
280 {"skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
281 {"translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType},
282 {"radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
283 {"radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
284 {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType},
285 {"local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType}}; // Conditional.
286
287 static constexpr int kColorAttribIdx = 4;
288
289 const Flags fFlags;
290
291 class Impl;
292 };
293
294 constexpr GrPrimitiveProcessor::Attribute GrAAFillRRectOp::Processor::kVertexAttribs[];
295 constexpr GrPrimitiveProcessor::Attribute GrAAFillRRectOp::Processor::kInstanceAttribs[];
296
297 class GrAAFillRRectOp::Processor::Impl : public GrGLSLGeometryProcessor {
298 public:
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)299 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
300 const auto& proc = args.fGP.cast<Processor>();
301 bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives);
302
303 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
304 varyings->emitAttributes(proc);
305 varyings->addPassThroughAttribute(proc.kInstanceAttribs[kColorAttribIdx], args.fOutputColor,
306 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
307
308 // Emit the vertex shader.
309 GrGLSLVertexBuilder* v = args.fVertBuilder;
310
311 // Unpack vertex attribs.
312 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
313 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
314 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
315 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
316 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
317
318 // Find the amount to bloat each edge for AA (in source space).
319 v->codeAppend("float2 pixellength = inversesqrt("
320 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
321 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
322 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
323 "abs(normalized_axis_dirs.zw));");
324 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
325
326 // Identify our radii.
327 v->codeAppend("float4 radii_and_neighbors = radii_selector"
328 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
329 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
330 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
331
332 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
333 // The rrect is more narrow than an AA coverage ramp. We can't draw as-is
334 // or else opposite AA borders will overlap. Instead, fudge the size up to
335 // the width of a coverage ramp, and then reduce total coverage to make
336 // the rect appear more thin.
337 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
338 v->codeAppend( "coverage /= max(aa_bloatradius.x, 1) * max(aa_bloatradius.y, 1);");
339 // Set radii to zero to ensure we take the "linear coverage" codepath.
340 // (The "coverage" variable only has effect in the linear codepath.)
341 v->codeAppend( "radii = float2(0);");
342 v->codeAppend("}");
343
344 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.25))) {");
345 // The radii are very small. Demote this arc to a sharp 90 degree corner.
346 v->codeAppend( "radii = aa_bloatradius;");
347 // Snap octagon vertices to the corner of the bounding box.
348 v->codeAppend( "radius_outset = floor(abs(radius_outset)) * radius_outset;");
349 v->codeAppend( "is_linear_coverage = 1;");
350 v->codeAppend("} else {");
351 // Don't let radii get smaller than a pixel.
352 v->codeAppend( "radii = clamp(radii, pixellength, 2 - pixellength);");
353 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength, 2 - pixellength);");
354 // Don't let neighboring radii get closer together than 1/16 pixel.
355 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
356 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
357 v->codeAppend( "radii -= extra_pad * .5;");
358 v->codeAppend("}");
359
360 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
361 // normalized [-1,-1,+1,+1] space.
362 v->codeAppend("float2 aa_outset = aa_bloat_direction.xy * aa_bloatradius;");
363 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
364
365 // Emit transforms.
366 GrShaderVar localCoord("", kFloat2_GrSLType);
367 if (proc.fFlags & Flags::kHasLocalCoords) {
368 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
369 "local_rect.zw * (1 + vertexpos)) * .5;");
370 localCoord.set(kFloat2_GrSLType, "localcoord");
371 }
372 this->emitTransforms(v, varyings, args.fUniformHandler, localCoord,
373 args.fFPCoordTransformHandler);
374
375 // Transform to device space.
376 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
377 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
378 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
379
380 // Setup interpolants for coverage.
381 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
382 varyings->addVarying("arccoord", &arcCoord);
383 v->codeAppend("if (0 != is_linear_coverage) {");
384 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
385 // interpolate linear coverage across y.
386 v->codeAppendf( "%s.xy = float2(0, coverage);", arcCoord.vsOut());
387 v->codeAppend("} else {");
388 // Find the normalized arc coordinates for our corner ellipse.
389 // (i.e., the coordinate system where x^2 + y^2 == 1).
390 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
391 // We are a corner piece: Interpolate the arc coordinates for coverage.
392 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
393 // instructs the fragment shader to use linear coverage).
394 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
395 if (!useHWDerivatives) {
396 // The gradient is order-1: Interpolate it across arccoord.zw.
397 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
398 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
399 }
400 v->codeAppend("}");
401
402 // Emit the fragment shader.
403 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
404
405 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
406 f->codeAppendf("half coverage;");
407 f->codeAppendf("if (0 == x_plus_1) {");
408 f->codeAppendf( "coverage = y;"); // We are a non-arc pixel (i.e., linear coverage).
409 f->codeAppendf("} else {");
410 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
411 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
412 if (useHWDerivatives) {
413 f->codeAppendf("float fnwidth = fwidth(fn);");
414 } else {
415 // The gradient is interpolated across arccoord.zw.
416 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
417 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
418 }
419 f->codeAppendf( "half d = fn/fnwidth;");
420 f->codeAppendf( "coverage = clamp(.5 - d, 0, 1);");
421 f->codeAppendf("}");
422 f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage);
423 }
424
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor &,FPCoordTransformIter && transformIter)425 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
426 FPCoordTransformIter&& transformIter) override {
427 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
428 }
429 };
430
createGLSLInstance(const GrShaderCaps &) const431 GrGLSLPrimitiveProcessor* GrAAFillRRectOp::Processor::createGLSLInstance(
432 const GrShaderCaps&) const {
433 return new Impl();
434 }
435
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)436 void GrAAFillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
437 if (!fInstanceBuffer) {
438 return; // Setup failed.
439 }
440
441 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
442
443 sk_sp<const GrBuffer> indexBuffer =
444 flushState->resourceProvider()->findOrMakeStaticBuffer(
445 kIndex_GrBufferType, sizeof(kIndexData), kIndexData, gIndexBufferKey);
446 if (!indexBuffer) {
447 return;
448 }
449
450 GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
451
452 sk_sp<const GrBuffer> vertexBuffer =
453 flushState->resourceProvider()->findOrMakeStaticBuffer(
454 kVertex_GrBufferType, sizeof(kVertexData), kVertexData, gVertexBufferKey);
455 if (!vertexBuffer) {
456 return;
457 }
458
459 Processor proc(fFlags);
460 SkASSERT(proc.instanceStride() == (size_t)fInstanceStride);
461
462 GrPipeline::InitArgs initArgs;
463 initArgs.fCaps = &flushState->caps();
464 initArgs.fResourceProvider = flushState->resourceProvider();
465 initArgs.fDstProxy = flushState->drawOpArgs().fDstProxy;
466 auto clip = flushState->detachAppliedClip();
467 GrPipeline::FixedDynamicState fixedDynamicState(clip.scissorState().rect());
468 GrPipeline pipeline(initArgs, std::move(fProcessors), std::move(clip));
469
470 GrMesh mesh(GrPrimitiveType::kTriangles);
471 mesh.setIndexedInstanced(std::move(indexBuffer), SK_ARRAY_COUNT(kIndexData), fInstanceBuffer,
472 fInstanceCount, fBaseInstance, GrPrimitiveRestart::kNo);
473 mesh.setVertexData(std::move(vertexBuffer));
474 flushState->rtCommandBuffer()->draw(proc, pipeline, &fixedDynamicState, nullptr, &mesh, 1,
475 this->bounds());
476 }
477
478 // Will the given corner look good if we use HW derivatives?
can_use_hw_derivatives(const Sk2f & devScale,const Sk2f & cornerRadii)479 static bool can_use_hw_derivatives(const Sk2f& devScale, const Sk2f& cornerRadii) {
480 Sk2f devRadii = devScale * cornerRadii;
481 if (devRadii[1] < devRadii[0]) {
482 devRadii = SkNx_shuffle<1,0>(devRadii);
483 }
484 float minDevRadius = SkTMax(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
485 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
486 // This threshold was arrived at subjevtively on an NVIDIA chip.
487 return minDevRadius * minDevRadius * 5 > devRadii[1];
488 }
489
can_use_hw_derivatives(const Sk2f & devScale,const SkVector & cornerRadii)490 static bool can_use_hw_derivatives(const Sk2f& devScale, const SkVector& cornerRadii) {
491 return can_use_hw_derivatives(devScale, Sk2f::Load(&cornerRadii));
492 }
493
494 // Will the given round rect look good if we use HW derivatives?
can_use_hw_derivatives(const GrShaderCaps & shaderCaps,const SkMatrix & viewMatrix,const SkRRect & rrect)495 static bool can_use_hw_derivatives(const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
496 const SkRRect& rrect) {
497 if (!shaderCaps.shaderDerivativeSupport()) {
498 return false;
499 }
500
501 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
502 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
503 Sk2f devScale = (x*x + y*y).sqrt();
504 switch (rrect.getType()) {
505 case SkRRect::kEmpty_Type:
506 case SkRRect::kRect_Type:
507 return true;
508
509 case SkRRect::kOval_Type:
510 case SkRRect::kSimple_Type:
511 return can_use_hw_derivatives(devScale, rrect.getSimpleRadii());
512
513 case SkRRect::kNinePatch_Type: {
514 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
515 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
516 Sk2f minRadii = Sk2f::Min(r0, r1);
517 Sk2f maxRadii = Sk2f::Max(r0, r1);
518 return can_use_hw_derivatives(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
519 can_use_hw_derivatives(devScale, Sk2f(maxRadii[0], minRadii[1]));
520 }
521
522 case SkRRect::kComplex_Type: {
523 for (int i = 0; i < 4; ++i) {
524 auto corner = static_cast<SkRRect::Corner>(i);
525 if (!can_use_hw_derivatives(devScale, rrect.radii(corner))) {
526 return false;
527 }
528 }
529 return true;
530 }
531 }
532 SK_ABORT("Unreachable code.");
533 return false; // Add this return to keep GCC happy.
534 }
535