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