blob: 28854145baaf6b181e42971ef1d120f6fadc85d5 [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},
Chris Dalton2d07e862018-11-26 12:30:47 -0700208 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700209 {{{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.
Mike Reedd3efa992018-11-28 13:13:15 +0000328 v->codeAppend("float4 radii_and_neighbors = radii_selector"
329 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
330 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
331 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500332
333 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
334 // The rrect is more narrow than an AA coverage ramp. We can't draw as-is
335 // or else opposite AA borders will overlap. Instead, fudge the size up to
336 // the width of a coverage ramp, and then reduce total coverage to make
337 // the rect appear more thin.
338 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
339 v->codeAppend( "coverage /= max(aa_bloatradius.x, 1) * max(aa_bloatradius.y, 1);");
340 // Set radii to zero to ensure we take the "linear coverage" codepath.
341 // (The "coverage" variable only has effect in the linear codepath.)
342 v->codeAppend( "radii = float2(0);");
343 v->codeAppend("}");
344
345 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.25))) {");
346 // The radii are very small. Demote this arc to a sharp 90 degree corner.
347 v->codeAppend( "radii = aa_bloatradius;");
348 // Snap octagon vertices to the corner of the bounding box.
349 v->codeAppend( "radius_outset = floor(abs(radius_outset)) * radius_outset;");
350 v->codeAppend( "is_linear_coverage = 1;");
351 v->codeAppend("} else {");
Mike Reedd3efa992018-11-28 13:13:15 +0000352 // Don't let radii get smaller than a pixel.
Chris Dalton133944a2018-11-16 23:30:29 -0500353 v->codeAppend( "radii = clamp(radii, pixellength, 2 - pixellength);");
Mike Reedd3efa992018-11-28 13:13:15 +0000354 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength, 2 - pixellength);");
355 // Don't let neighboring radii get closer together than 1/16 pixel.
356 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
357 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
358 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500359 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500360
361 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
362 // normalized [-1,-1,+1,+1] space.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700363 v->codeAppend("float2 aa_outset = aa_bloat_direction.xy * aa_bloatradius;");
364 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500365
366 // Emit transforms.
367 GrShaderVar localCoord("", kFloat2_GrSLType);
368 if (proc.fFlags & Flags::kHasLocalCoords) {
369 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
370 "local_rect.zw * (1 + vertexpos)) * .5;");
371 localCoord.set(kFloat2_GrSLType, "localcoord");
372 }
373 this->emitTransforms(v, varyings, args.fUniformHandler, localCoord,
374 args.fFPCoordTransformHandler);
375
376 // Transform to device space.
377 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
378 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
379 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
380
381 // Setup interpolants for coverage.
382 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
383 varyings->addVarying("arccoord", &arcCoord);
384 v->codeAppend("if (0 != is_linear_coverage) {");
385 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
386 // interpolate linear coverage across y.
387 v->codeAppendf( "%s.xy = float2(0, coverage);", arcCoord.vsOut());
388 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700389 // Find the normalized arc coordinates for our corner ellipse.
390 // (i.e., the coordinate system where x^2 + y^2 == 1).
391 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500392 // We are a corner piece: Interpolate the arc coordinates for coverage.
393 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
394 // instructs the fragment shader to use linear coverage).
395 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
396 if (!useHWDerivatives) {
397 // The gradient is order-1: Interpolate it across arccoord.zw.
398 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
399 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
400 }
401 v->codeAppend("}");
402
403 // Emit the fragment shader.
404 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
405
406 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
407 f->codeAppendf("half coverage;");
408 f->codeAppendf("if (0 == x_plus_1) {");
409 f->codeAppendf( "coverage = y;"); // We are a non-arc pixel (i.e., linear coverage).
410 f->codeAppendf("} else {");
411 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
412 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
413 if (useHWDerivatives) {
414 f->codeAppendf("float fnwidth = fwidth(fn);");
415 } else {
416 // The gradient is interpolated across arccoord.zw.
417 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
418 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
419 }
420 f->codeAppendf( "half d = fn/fnwidth;");
421 f->codeAppendf( "coverage = clamp(.5 - d, 0, 1);");
422 f->codeAppendf("}");
423 f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage);
424 }
425
426 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
427 FPCoordTransformIter&& transformIter) override {
428 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
429 }
430};
431
432GrGLSLPrimitiveProcessor* GrAAFillRRectOp::Processor::createGLSLInstance(
433 const GrShaderCaps&) const {
434 return new Impl();
435}
436
437void GrAAFillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
438 if (!fInstanceBuffer) {
439 return; // Setup failed.
440 }
441
442 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
443
444 sk_sp<const GrBuffer> indexBuffer =
445 flushState->resourceProvider()->findOrMakeStaticBuffer(
446 kIndex_GrBufferType, sizeof(kIndexData), kIndexData, gIndexBufferKey);
447 if (!indexBuffer) {
448 return;
449 }
450
451 GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
452
453 sk_sp<const GrBuffer> vertexBuffer =
454 flushState->resourceProvider()->findOrMakeStaticBuffer(
455 kVertex_GrBufferType, sizeof(kVertexData), kVertexData, gVertexBufferKey);
456 if (!vertexBuffer) {
457 return;
458 }
459
460 Processor proc(fFlags);
461 SkASSERT(proc.instanceStride() == (size_t)fInstanceStride);
462
463 GrPipeline::InitArgs initArgs;
464 initArgs.fProxy = flushState->drawOpArgs().fProxy;
465 initArgs.fCaps = &flushState->caps();
466 initArgs.fResourceProvider = flushState->resourceProvider();
467 initArgs.fDstProxy = flushState->drawOpArgs().fDstProxy;
Chris Dalton8fa16252018-11-19 13:37:31 -0700468 auto clip = flushState->detachAppliedClip();
469 GrPipeline::FixedDynamicState fixedDynamicState(clip.scissorState().rect());
470 GrPipeline pipeline(initArgs, std::move(fProcessors), std::move(clip));
Chris Dalton133944a2018-11-16 23:30:29 -0500471
472 GrMesh mesh(GrPrimitiveType::kTriangles);
473 mesh.setIndexedInstanced(indexBuffer.get(), SK_ARRAY_COUNT(kIndexData), fInstanceBuffer,
474 fInstanceCount, fBaseInstance, GrPrimitiveRestart::kNo);
475 mesh.setVertexData(vertexBuffer.get());
Chris Dalton8fa16252018-11-19 13:37:31 -0700476 flushState->rtCommandBuffer()->draw(proc, pipeline, &fixedDynamicState, nullptr, &mesh, 1,
477 this->bounds());
Chris Dalton133944a2018-11-16 23:30:29 -0500478}
479
480// Will the given corner look good if we use HW derivatives?
481static bool can_use_hw_derivatives(const Sk2f& devScale, const Sk2f& cornerRadii) {
482 Sk2f devRadii = devScale * cornerRadii;
483 if (devRadii[1] < devRadii[0]) {
484 devRadii = SkNx_shuffle<1,0>(devRadii);
485 }
486 float minDevRadius = SkTMax(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
487 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
488 // This threshold was arrived at subjevtively on an NVIDIA chip.
489 return minDevRadius * minDevRadius * 5 > devRadii[1];
490}
491
492static bool can_use_hw_derivatives(const Sk2f& devScale, const SkVector& cornerRadii) {
493 return can_use_hw_derivatives(devScale, Sk2f::Load(&cornerRadii));
494}
495
496// Will the given round rect look good if we use HW derivatives?
497static bool can_use_hw_derivatives(const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
498 const SkRRect& rrect) {
499 if (!shaderCaps.shaderDerivativeSupport()) {
500 return false;
501 }
502
503 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
504 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
505 Sk2f devScale = (x*x + y*y).sqrt();
506 switch (rrect.getType()) {
507 case SkRRect::kEmpty_Type:
508 case SkRRect::kRect_Type:
509 return true;
510
511 case SkRRect::kOval_Type:
512 case SkRRect::kSimple_Type:
513 return can_use_hw_derivatives(devScale, rrect.getSimpleRadii());
514
515 case SkRRect::kNinePatch_Type: {
516 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
517 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
518 Sk2f minRadii = Sk2f::Min(r0, r1);
519 Sk2f maxRadii = Sk2f::Max(r0, r1);
520 return can_use_hw_derivatives(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
521 can_use_hw_derivatives(devScale, Sk2f(maxRadii[0], minRadii[1]));
522 }
523
524 case SkRRect::kComplex_Type: {
525 for (int i = 0; i < 4; ++i) {
526 auto corner = static_cast<SkRRect::Corner>(i);
527 if (!can_use_hw_derivatives(devScale, rrect.radii(corner))) {
528 return false;
529 }
530 }
531 return true;
532 }
533 }
534 SK_ABORT("Unreachable code.");
535 return false; // Add this return to keep GCC happy.
536}