blob: 1b24fc0fcb9d5a4097799479b6c255281e0a6e92 [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
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.
22static bool can_use_hw_derivatives(const GrShaderCaps&, const SkMatrix&, const SkRRect&);
23
24std::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
42GrAAFillRRectOp::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
82GrDrawOp::RequiresDstTexture GrAAFillRRectOp::finalize(const GrCaps& caps,
83 const GrAppliedClip* clip) {
84 SkASSERT(1 == fInstanceCount);
85
86 SkPMColor4f overrideColor;
87 const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
88 fOriginalColor, GrProcessorAnalysisCoverage::kSingleChannel, clip, false, caps,
89 &overrideColor);
90
91 // Finish writing the instance attribs.
92 this->writeInstanceData(
93 (analysis.inputColorIsOverridden() ? overrideColor : fOriginalColor).toBytes_RGBA());
94 if (analysis.usesLocalCoords()) {
95 this->writeInstanceData(fLocalRect);
96 fFlags |= Flags::kHasLocalCoords;
97 }
98 fInstanceStride = fInstanceData.count();
99
100 return RequiresDstTexture(analysis.requiresDstTexture());
101}
102
103GrDrawOp::CombineResult GrAAFillRRectOp::onCombineIfPossible(GrOp* op, const GrCaps&) {
104 const auto& that = *op->cast<GrAAFillRRectOp>();
105 if (fFlags != that.fFlags || fProcessors != that.fProcessors ||
106 fInstanceData.count() > std::numeric_limits<int>::max() - that.fInstanceData.count()) {
107 return CombineResult::kCannotCombine;
108 }
109
110 fInstanceData.push_back_n(that.fInstanceData.count(), that.fInstanceData.begin());
111 fInstanceCount += that.fInstanceCount;
112 SkASSERT(fInstanceStride == that.fInstanceStride);
113 return CombineResult::kMerged;
114}
115
116void GrAAFillRRectOp::onPrepare(GrOpFlushState* flushState) {
117 if (void* instanceData = flushState->makeVertexSpace(fInstanceStride, fInstanceCount,
118 &fInstanceBuffer, &fBaseInstance)) {
119 SkASSERT(fInstanceStride * fInstanceCount == fInstanceData.count());
120 memcpy(instanceData, fInstanceData.begin(), fInstanceData.count());
121 }
122}
123
124namespace {
125
126// Our round rect geometry consists of an inset octagon with solid coverage, surrounded by linear
127// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
128// edges. The Vertex struct tells the shader where to place its vertex within a normalized
129// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
130struct Vertex {
131 std::array<float, 4> fRadiiSelector;
132 std::array<float, 2> fCorner;
133 std::array<float, 2> fRadiusOutset;
134 std::array<float, 2> fAABloatDirection;
135 float fCoverage;
136 float fIsLinearCoverage;
Chris Dalton133944a2018-11-16 23:30:29 -0500137};
138
139// This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
140// of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
141// rectangles.
142static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
143
Chris Dalton133944a2018-11-16 23:30:29 -0500144static constexpr Vertex kVertexData[] = {
145 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700146 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
147 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500148
149 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700150 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
151 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500152
153 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700154 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
155 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500156
157 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700158 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
159 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500160
161
162 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700163 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
164 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500165
166 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700167 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
168 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500169
170 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700171 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
172 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500173
174 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700175 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
176 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500177
178
179 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700180 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
181 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
182 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
183 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
184 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
185 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500186
187 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700188 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
189 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
190 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
191 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
192 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
193 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500194
195 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700196 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
197 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
198 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
199 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
200 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
201 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500202
203 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700204 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
205 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
206 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
207 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
208 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1, 0}}, 0, 0},
209 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500210
211GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);
212
213static constexpr uint16_t kIndexData[] = {
214 // Inset octagon (solid coverage).
215 0, 1, 7,
216 1, 2, 7,
217 7, 2, 6,
218 2, 3, 6,
219 6, 3, 5,
220 3, 4, 5,
221
222 // AA borders (linear coverage).
223 0, 1, 8, 1, 9, 8,
224 2, 3, 10, 3, 11, 10,
225 4, 5, 12, 5, 13, 12,
226 6, 7, 14, 7, 15, 14,
227
228 // Top-left arc.
229 16, 17, 21,
230 17, 21, 18,
231 21, 18, 20,
232 18, 20, 19,
233
234 // Top-right arc.
235 22, 23, 27,
236 23, 27, 24,
237 27, 24, 26,
238 24, 26, 25,
239
240 // Bottom-right arc.
241 28, 29, 33,
242 29, 33, 30,
243 33, 30, 32,
244 30, 32, 31,
245
246 // Bottom-left arc.
247 34, 35, 39,
248 35, 39, 36,
249 39, 36, 38,
250 36, 38, 37};
251
252GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
253
254}
255
256class GrAAFillRRectOp::Processor : public GrGeometryProcessor {
257public:
258 Processor(Flags flags)
259 : GrGeometryProcessor(kGrAAFillRRectOp_Processor_ClassID)
260 , fFlags(flags) {
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700261 this->setVertexAttributes(kVertexAttribs, 3);
Chris Dalton133944a2018-11-16 23:30:29 -0500262 this->setInstanceAttributes(kInstanceAttribs, (flags & Flags::kHasLocalCoords) ? 6 : 5);
263 SkASSERT(this->vertexStride() == sizeof(Vertex));
264 }
265
266 const char* name() const override { return "GrAAFillRRectOp::Processor"; }
267
268 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
269 b->add32(static_cast<uint32_t>(fFlags));
270 }
271
272 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
273
274private:
275 static constexpr Attribute kVertexAttribs[] = {
276 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
277 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700278 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
Chris Dalton133944a2018-11-16 23:30:29 -0500279
280 static constexpr Attribute kInstanceAttribs[] = {
281 {"skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
282 {"translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType},
283 {"radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
284 {"radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
285 {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType},
286 {"local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType}}; // Conditional.
287
288 static constexpr int kColorAttribIdx = 4;
289
290 const Flags fFlags;
291
292 class Impl;
293};
294
295constexpr GrPrimitiveProcessor::Attribute GrAAFillRRectOp::Processor::kVertexAttribs[];
296constexpr GrPrimitiveProcessor::Attribute GrAAFillRRectOp::Processor::kInstanceAttribs[];
297
298class GrAAFillRRectOp::Processor::Impl : public GrGLSLGeometryProcessor {
299public:
300 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
301 const auto& proc = args.fGP.cast<Processor>();
302 bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives);
303
304 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
305 varyings->emitAttributes(proc);
306 varyings->addPassThroughAttribute(proc.kInstanceAttribs[kColorAttribIdx], args.fOutputColor,
307 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
308
309 // Emit the vertex shader.
310 GrGLSLVertexBuilder* v = args.fVertBuilder;
311
312 // Unpack vertex attribs.
313 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
314 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
315 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
316 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
317 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
318
319 // Find the amount to bloat each edge for AA (in source space).
320 v->codeAppend("float2 pixellength = inversesqrt("
321 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
322 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
323 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
324 "abs(normalized_axis_dirs.zw));");
325 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
326
327 // Identify our radii.
328 v->codeAppend("float2 radii = float2(dot(radii_selector, radii_x), "
329 "dot(radii_selector, radii_y));");
330
331 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
332 // The rrect is more narrow than an AA coverage ramp. We can't draw as-is
333 // or else opposite AA borders will overlap. Instead, fudge the size up to
334 // the width of a coverage ramp, and then reduce total coverage to make
335 // the rect appear more thin.
336 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
337 v->codeAppend( "coverage /= max(aa_bloatradius.x, 1) * max(aa_bloatradius.y, 1);");
338 // Set radii to zero to ensure we take the "linear coverage" codepath.
339 // (The "coverage" variable only has effect in the linear codepath.)
340 v->codeAppend( "radii = float2(0);");
341 v->codeAppend("}");
342
343 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.25))) {");
344 // The radii are very small. Demote this arc to a sharp 90 degree corner.
345 v->codeAppend( "radii = aa_bloatradius;");
346 // Snap octagon vertices to the corner of the bounding box.
347 v->codeAppend( "radius_outset = floor(abs(radius_outset)) * radius_outset;");
348 v->codeAppend( "is_linear_coverage = 1;");
349 v->codeAppend("} else {");
350 // Don't let actual arc radii get smaller than a pixel.
351 v->codeAppend( "radii = clamp(radii, pixellength, 2 - pixellength);");
352 v->codeAppend("}");
353 // Bias radii slightly inward to avoid accidental overlap of geometries from fp rounding.
354 v->codeAppend("radii -= aa_bloatradius * 1e-3;");
355
356 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
357 // normalized [-1,-1,+1,+1] space.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700358 v->codeAppend("float2 aa_outset = aa_bloat_direction.xy * aa_bloatradius;");
359 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500360
361 // Emit transforms.
362 GrShaderVar localCoord("", kFloat2_GrSLType);
363 if (proc.fFlags & Flags::kHasLocalCoords) {
364 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
365 "local_rect.zw * (1 + vertexpos)) * .5;");
366 localCoord.set(kFloat2_GrSLType, "localcoord");
367 }
368 this->emitTransforms(v, varyings, args.fUniformHandler, localCoord,
369 args.fFPCoordTransformHandler);
370
371 // Transform to device space.
372 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
373 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
374 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
375
376 // Setup interpolants for coverage.
377 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
378 varyings->addVarying("arccoord", &arcCoord);
379 v->codeAppend("if (0 != is_linear_coverage) {");
380 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
381 // interpolate linear coverage across y.
382 v->codeAppendf( "%s.xy = float2(0, coverage);", arcCoord.vsOut());
383 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700384 // Find the normalized arc coordinates for our corner ellipse.
385 // (i.e., the coordinate system where x^2 + y^2 == 1).
386 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500387 // We are a corner piece: Interpolate the arc coordinates for coverage.
388 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
389 // instructs the fragment shader to use linear coverage).
390 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
391 if (!useHWDerivatives) {
392 // The gradient is order-1: Interpolate it across arccoord.zw.
393 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
394 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
395 }
396 v->codeAppend("}");
397
398 // Emit the fragment shader.
399 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
400
401 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
402 f->codeAppendf("half coverage;");
403 f->codeAppendf("if (0 == x_plus_1) {");
404 f->codeAppendf( "coverage = y;"); // We are a non-arc pixel (i.e., linear coverage).
405 f->codeAppendf("} else {");
406 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
407 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
408 if (useHWDerivatives) {
409 f->codeAppendf("float fnwidth = fwidth(fn);");
410 } else {
411 // The gradient is interpolated across arccoord.zw.
412 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
413 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
414 }
415 f->codeAppendf( "half d = fn/fnwidth;");
416 f->codeAppendf( "coverage = clamp(.5 - d, 0, 1);");
417 f->codeAppendf("}");
418 f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage);
419 }
420
421 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
422 FPCoordTransformIter&& transformIter) override {
423 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
424 }
425};
426
427GrGLSLPrimitiveProcessor* GrAAFillRRectOp::Processor::createGLSLInstance(
428 const GrShaderCaps&) const {
429 return new Impl();
430}
431
432void GrAAFillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
433 if (!fInstanceBuffer) {
434 return; // Setup failed.
435 }
436
437 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
438
439 sk_sp<const GrBuffer> indexBuffer =
440 flushState->resourceProvider()->findOrMakeStaticBuffer(
441 kIndex_GrBufferType, sizeof(kIndexData), kIndexData, gIndexBufferKey);
442 if (!indexBuffer) {
443 return;
444 }
445
446 GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
447
448 sk_sp<const GrBuffer> vertexBuffer =
449 flushState->resourceProvider()->findOrMakeStaticBuffer(
450 kVertex_GrBufferType, sizeof(kVertexData), kVertexData, gVertexBufferKey);
451 if (!vertexBuffer) {
452 return;
453 }
454
455 Processor proc(fFlags);
456 SkASSERT(proc.instanceStride() == (size_t)fInstanceStride);
457
458 GrPipeline::InitArgs initArgs;
459 initArgs.fProxy = flushState->drawOpArgs().fProxy;
460 initArgs.fCaps = &flushState->caps();
461 initArgs.fResourceProvider = flushState->resourceProvider();
462 initArgs.fDstProxy = flushState->drawOpArgs().fDstProxy;
Chris Dalton8fa16252018-11-19 13:37:31 -0700463 auto clip = flushState->detachAppliedClip();
464 GrPipeline::FixedDynamicState fixedDynamicState(clip.scissorState().rect());
465 GrPipeline pipeline(initArgs, std::move(fProcessors), std::move(clip));
Chris Dalton133944a2018-11-16 23:30:29 -0500466
467 GrMesh mesh(GrPrimitiveType::kTriangles);
468 mesh.setIndexedInstanced(indexBuffer.get(), SK_ARRAY_COUNT(kIndexData), fInstanceBuffer,
469 fInstanceCount, fBaseInstance, GrPrimitiveRestart::kNo);
470 mesh.setVertexData(vertexBuffer.get());
Chris Dalton8fa16252018-11-19 13:37:31 -0700471 flushState->rtCommandBuffer()->draw(proc, pipeline, &fixedDynamicState, nullptr, &mesh, 1,
472 this->bounds());
Chris Dalton133944a2018-11-16 23:30:29 -0500473}
474
475// Will the given corner look good if we use HW derivatives?
476static bool can_use_hw_derivatives(const Sk2f& devScale, const Sk2f& cornerRadii) {
477 Sk2f devRadii = devScale * cornerRadii;
478 if (devRadii[1] < devRadii[0]) {
479 devRadii = SkNx_shuffle<1,0>(devRadii);
480 }
481 float minDevRadius = SkTMax(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
482 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
483 // This threshold was arrived at subjevtively on an NVIDIA chip.
484 return minDevRadius * minDevRadius * 5 > devRadii[1];
485}
486
487static bool can_use_hw_derivatives(const Sk2f& devScale, const SkVector& cornerRadii) {
488 return can_use_hw_derivatives(devScale, Sk2f::Load(&cornerRadii));
489}
490
491// Will the given round rect look good if we use HW derivatives?
492static bool can_use_hw_derivatives(const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
493 const SkRRect& rrect) {
494 if (!shaderCaps.shaderDerivativeSupport()) {
495 return false;
496 }
497
498 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
499 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
500 Sk2f devScale = (x*x + y*y).sqrt();
501 switch (rrect.getType()) {
502 case SkRRect::kEmpty_Type:
503 case SkRRect::kRect_Type:
504 return true;
505
506 case SkRRect::kOval_Type:
507 case SkRRect::kSimple_Type:
508 return can_use_hw_derivatives(devScale, rrect.getSimpleRadii());
509
510 case SkRRect::kNinePatch_Type: {
511 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
512 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
513 Sk2f minRadii = Sk2f::Min(r0, r1);
514 Sk2f maxRadii = Sk2f::Max(r0, r1);
515 return can_use_hw_derivatives(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
516 can_use_hw_derivatives(devScale, Sk2f(maxRadii[0], minRadii[1]));
517 }
518
519 case SkRRect::kComplex_Type: {
520 for (int i = 0; i < 4; ++i) {
521 auto corner = static_cast<SkRRect::Corner>(i);
522 if (!can_use_hw_derivatives(devScale, rrect.radii(corner))) {
523 return false;
524 }
525 }
526 return true;
527 }
528 }
529 SK_ABORT("Unreachable code.");
530 return false; // Add this return to keep GCC happy.
531}