blob: 464462fb4901ab78f4acb35495f8f49bc404d422 [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
Robert Phillips9da87e02019-02-04 13:26:26 -050038 GrOpMemoryPool* pool = ctx->priv().opMemoryPool();
Chris Dalton133944a2018-11-16 23:30:29 -050039 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) {");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500408 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (i.e., linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500409 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 }
Ethan Nicholase1f55022019-02-05 17:17:40 -0500419 f->codeAppendf( "half d = half(fn/fnwidth);");
Chris Dalton133944a2018-11-16 23:30:29 -0500420 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
Brian Salomonae64c192019-02-05 09:41:37 -0500443 sk_sp<const GrBuffer> indexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
444 GrGpuBufferType::kIndex, sizeof(kIndexData), kIndexData, gIndexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500445 if (!indexBuffer) {
446 return;
447 }
448
449 GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
450
Brian Salomonae64c192019-02-05 09:41:37 -0500451 sk_sp<const GrBuffer> vertexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
452 GrGpuBufferType::kVertex, sizeof(kVertexData), kVertexData, gVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500453 if (!vertexBuffer) {
454 return;
455 }
456
457 Processor proc(fFlags);
458 SkASSERT(proc.instanceStride() == (size_t)fInstanceStride);
459
460 GrPipeline::InitArgs initArgs;
Chris Dalton133944a2018-11-16 23:30:29 -0500461 initArgs.fCaps = &flushState->caps();
462 initArgs.fResourceProvider = flushState->resourceProvider();
463 initArgs.fDstProxy = flushState->drawOpArgs().fDstProxy;
Chris Dalton8fa16252018-11-19 13:37:31 -0700464 auto clip = flushState->detachAppliedClip();
465 GrPipeline::FixedDynamicState fixedDynamicState(clip.scissorState().rect());
466 GrPipeline pipeline(initArgs, std::move(fProcessors), std::move(clip));
Chris Dalton133944a2018-11-16 23:30:29 -0500467
468 GrMesh mesh(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500469 mesh.setIndexedInstanced(std::move(indexBuffer), SK_ARRAY_COUNT(kIndexData), fInstanceBuffer,
Chris Dalton133944a2018-11-16 23:30:29 -0500470 fInstanceCount, fBaseInstance, GrPrimitiveRestart::kNo);
Brian Salomon12d22642019-01-29 14:38:50 -0500471 mesh.setVertexData(std::move(vertexBuffer));
Chris Dalton8fa16252018-11-19 13:37:31 -0700472 flushState->rtCommandBuffer()->draw(proc, pipeline, &fixedDynamicState, nullptr, &mesh, 1,
473 this->bounds());
Chris Dalton133944a2018-11-16 23:30:29 -0500474}
475
476// Will the given corner look good if we use HW derivatives?
477static bool can_use_hw_derivatives(const Sk2f& devScale, const Sk2f& cornerRadii) {
478 Sk2f devRadii = devScale * cornerRadii;
479 if (devRadii[1] < devRadii[0]) {
480 devRadii = SkNx_shuffle<1,0>(devRadii);
481 }
482 float minDevRadius = SkTMax(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
483 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
484 // This threshold was arrived at subjevtively on an NVIDIA chip.
485 return minDevRadius * minDevRadius * 5 > devRadii[1];
486}
487
488static bool can_use_hw_derivatives(const Sk2f& devScale, const SkVector& cornerRadii) {
489 return can_use_hw_derivatives(devScale, Sk2f::Load(&cornerRadii));
490}
491
492// Will the given round rect look good if we use HW derivatives?
493static bool can_use_hw_derivatives(const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
494 const SkRRect& rrect) {
495 if (!shaderCaps.shaderDerivativeSupport()) {
496 return false;
497 }
498
499 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
500 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
501 Sk2f devScale = (x*x + y*y).sqrt();
502 switch (rrect.getType()) {
503 case SkRRect::kEmpty_Type:
504 case SkRRect::kRect_Type:
505 return true;
506
507 case SkRRect::kOval_Type:
508 case SkRRect::kSimple_Type:
509 return can_use_hw_derivatives(devScale, rrect.getSimpleRadii());
510
511 case SkRRect::kNinePatch_Type: {
512 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
513 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
514 Sk2f minRadii = Sk2f::Min(r0, r1);
515 Sk2f maxRadii = Sk2f::Max(r0, r1);
516 return can_use_hw_derivatives(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
517 can_use_hw_derivatives(devScale, Sk2f(maxRadii[0], minRadii[1]));
518 }
519
520 case SkRRect::kComplex_Type: {
521 for (int i = 0; i < 4; ++i) {
522 auto corner = static_cast<SkRRect::Corner>(i);
523 if (!can_use_hw_derivatives(devScale, rrect.radii(corner))) {
524 return false;
525 }
526 }
527 return true;
528 }
529 }
530 SK_ABORT("Unreachable code.");
531 return false; // Add this return to keep GCC happy.
532}