blob: 513c15ba2ba2052bdd2da18fce315575d317d43a [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
Chris Dalton4b62aed2019-01-15 11:53:00 -070082GrProcessorSet::Analysis GrAAFillRRectOp::finalize(const GrCaps& caps, const GrAppliedClip* clip) {
Chris Dalton133944a2018-11-16 23:30:29 -050083 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
Chris Dalton4b62aed2019-01-15 11:53:00 -070099 return analysis;
Chris Dalton133944a2018-11-16 23:30:29 -0500100}
101
102GrDrawOp::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
115void 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
123namespace {
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.
129struct 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;
Chris Dalton133944a2018-11-16 23:30:29 -0500136};
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.
141static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
142
Chris Dalton133944a2018-11-16 23:30:29 -0500143static constexpr Vertex kVertexData[] = {
144 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700145 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500147
148 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700149 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500151
152 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700153 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500155
156 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700157 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500159
160
161 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700162 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500164
165 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700166 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500168
169 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700170 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500172
173 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700174 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500176
177
178 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700179 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500185
186 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700187 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500193
194 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700195 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500201
202 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700203 {{{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},
Chris Dalton2d07e862018-11-26 12:30:47 -0700207 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700208 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500209
210GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);
211
212static 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
251GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
252
253}
254
255class GrAAFillRRectOp::Processor : public GrGeometryProcessor {
256public:
257 Processor(Flags flags)
258 : GrGeometryProcessor(kGrAAFillRRectOp_Processor_ClassID)
259 , fFlags(flags) {
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700260 this->setVertexAttributes(kVertexAttribs, 3);
Chris Dalton133944a2018-11-16 23:30:29 -0500261 this->setInstanceAttributes(kInstanceAttribs, (flags & Flags::kHasLocalCoords) ? 6 : 5);
262 SkASSERT(this->vertexStride() == sizeof(Vertex));
263 }
264
265 const char* name() const override { return "GrAAFillRRectOp::Processor"; }
266
267 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
273private:
274 static constexpr Attribute kVertexAttribs[] = {
275 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
276 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700277 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
Chris Dalton133944a2018-11-16 23:30:29 -0500278
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
294constexpr GrPrimitiveProcessor::Attribute GrAAFillRRectOp::Processor::kVertexAttribs[];
295constexpr GrPrimitiveProcessor::Attribute GrAAFillRRectOp::Processor::kInstanceAttribs[];
296
297class GrAAFillRRectOp::Processor::Impl : public GrGLSLGeometryProcessor {
298public:
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.
Mike Reedd3efa992018-11-28 13:13:15 +0000327 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;");
Chris Dalton133944a2018-11-16 23:30:29 -0500331
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 {");
Mike Reedd3efa992018-11-28 13:13:15 +0000351 // Don't let radii get smaller than a pixel.
Chris Dalton133944a2018-11-16 23:30:29 -0500352 v->codeAppend( "radii = clamp(radii, pixellength, 2 - pixellength);");
Mike Reedd3efa992018-11-28 13:13:15 +0000353 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;");
Chris Dalton133944a2018-11-16 23:30:29 -0500358 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500359
360 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
361 // normalized [-1,-1,+1,+1] space.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700362 v->codeAppend("float2 aa_outset = aa_bloat_direction.xy * aa_bloatradius;");
363 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500364
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 {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700388 // 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;");
Chris Dalton133944a2018-11-16 23:30:29 -0500391 // 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
425 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
426 FPCoordTransformIter&& transformIter) override {
427 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
428 }
429};
430
431GrGLSLPrimitiveProcessor* GrAAFillRRectOp::Processor::createGLSLInstance(
432 const GrShaderCaps&) const {
433 return new Impl();
434}
435
436void 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.fProxy = flushState->drawOpArgs().fProxy;
464 initArgs.fCaps = &flushState->caps();
465 initArgs.fResourceProvider = flushState->resourceProvider();
466 initArgs.fDstProxy = flushState->drawOpArgs().fDstProxy;
Chris Dalton8fa16252018-11-19 13:37:31 -0700467 auto clip = flushState->detachAppliedClip();
468 GrPipeline::FixedDynamicState fixedDynamicState(clip.scissorState().rect());
469 GrPipeline pipeline(initArgs, std::move(fProcessors), std::move(clip));
Chris Dalton133944a2018-11-16 23:30:29 -0500470
471 GrMesh mesh(GrPrimitiveType::kTriangles);
472 mesh.setIndexedInstanced(indexBuffer.get(), SK_ARRAY_COUNT(kIndexData), fInstanceBuffer,
473 fInstanceCount, fBaseInstance, GrPrimitiveRestart::kNo);
474 mesh.setVertexData(vertexBuffer.get());
Chris Dalton8fa16252018-11-19 13:37:31 -0700475 flushState->rtCommandBuffer()->draw(proc, pipeline, &fixedDynamicState, nullptr, &mesh, 1,
476 this->bounds());
Chris Dalton133944a2018-11-16 23:30:29 -0500477}
478
479// Will the given corner look good if we use HW derivatives?
480static bool can_use_hw_derivatives(const Sk2f& devScale, const Sk2f& cornerRadii) {
481 Sk2f devRadii = devScale * cornerRadii;
482 if (devRadii[1] < devRadii[0]) {
483 devRadii = SkNx_shuffle<1,0>(devRadii);
484 }
485 float minDevRadius = SkTMax(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
486 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
487 // This threshold was arrived at subjevtively on an NVIDIA chip.
488 return minDevRadius * minDevRadius * 5 > devRadii[1];
489}
490
491static bool can_use_hw_derivatives(const Sk2f& devScale, const SkVector& cornerRadii) {
492 return can_use_hw_derivatives(devScale, Sk2f::Load(&cornerRadii));
493}
494
495// Will the given round rect look good if we use HW derivatives?
496static bool can_use_hw_derivatives(const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
497 const SkRRect& rrect) {
498 if (!shaderCaps.shaderDerivativeSupport()) {
499 return false;
500 }
501
502 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
503 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
504 Sk2f devScale = (x*x + y*y).sqrt();
505 switch (rrect.getType()) {
506 case SkRRect::kEmpty_Type:
507 case SkRRect::kRect_Type:
508 return true;
509
510 case SkRRect::kOval_Type:
511 case SkRRect::kSimple_Type:
512 return can_use_hw_derivatives(devScale, rrect.getSimpleRadii());
513
514 case SkRRect::kNinePatch_Type: {
515 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
516 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
517 Sk2f minRadii = Sk2f::Min(r0, r1);
518 Sk2f maxRadii = Sk2f::Max(r0, r1);
519 return can_use_hw_derivatives(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
520 can_use_hw_derivatives(devScale, Sk2f(maxRadii[0], minRadii[1]));
521 }
522
523 case SkRRect::kComplex_Type: {
524 for (int i = 0; i < 4; ++i) {
525 auto corner = static_cast<SkRRect::Corner>(i);
526 if (!can_use_hw_derivatives(devScale, rrect.radii(corner))) {
527 return false;
528 }
529 }
530 return true;
531 }
532 }
533 SK_ABORT("Unreachable code.");
534 return false; // Add this return to keep GCC happy.
535}