blob: a4a78c3802dae520a98527d295a161893612258a [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 Daltonb8fff0d2019-03-05 10:11:58 -070084GrProcessorSet::Analysis GrAAFillRRectOp::finalize(
85 const GrCaps& caps, const GrAppliedClip* clip, GrFSAAType fsaaType) {
Chris Dalton133944a2018-11-16 23:30:29 -050086 SkASSERT(1 == fInstanceCount);
87
88 SkPMColor4f overrideColor;
89 const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
Chris Daltonb8fff0d2019-03-05 10:11:58 -070090
91 fOriginalColor, GrProcessorAnalysisCoverage::kSingleChannel, clip,
92 &GrUserStencilSettings::kUnused, fsaaType, caps, &overrideColor);
Chris Dalton133944a2018-11-16 23:30:29 -050093
94 // Finish writing the instance attribs.
Brian Osman5105d682019-02-13 16:06:14 -050095 SkPMColor4f finalColor = analysis.inputColorIsOverridden() ? overrideColor : fOriginalColor;
96 if (!SkPMColor4fFitsInBytes(finalColor)) {
97 fFlags |= Flags::kWideColor;
98 uint32_t halfColor[2];
99 SkFloatToHalf_finite_ftz(Sk4f::Load(finalColor.vec())).store(&halfColor);
100 this->writeInstanceData(halfColor[0], halfColor[1]);
101 } else {
102 this->writeInstanceData(finalColor.toBytes_RGBA());
103 }
104
Chris Dalton133944a2018-11-16 23:30:29 -0500105 if (analysis.usesLocalCoords()) {
106 this->writeInstanceData(fLocalRect);
107 fFlags |= Flags::kHasLocalCoords;
108 }
109 fInstanceStride = fInstanceData.count();
110
Chris Dalton4b62aed2019-01-15 11:53:00 -0700111 return analysis;
Chris Dalton133944a2018-11-16 23:30:29 -0500112}
113
114GrDrawOp::CombineResult GrAAFillRRectOp::onCombineIfPossible(GrOp* op, const GrCaps&) {
115 const auto& that = *op->cast<GrAAFillRRectOp>();
116 if (fFlags != that.fFlags || fProcessors != that.fProcessors ||
117 fInstanceData.count() > std::numeric_limits<int>::max() - that.fInstanceData.count()) {
118 return CombineResult::kCannotCombine;
119 }
120
121 fInstanceData.push_back_n(that.fInstanceData.count(), that.fInstanceData.begin());
122 fInstanceCount += that.fInstanceCount;
123 SkASSERT(fInstanceStride == that.fInstanceStride);
124 return CombineResult::kMerged;
125}
126
127void GrAAFillRRectOp::onPrepare(GrOpFlushState* flushState) {
128 if (void* instanceData = flushState->makeVertexSpace(fInstanceStride, fInstanceCount,
129 &fInstanceBuffer, &fBaseInstance)) {
130 SkASSERT(fInstanceStride * fInstanceCount == fInstanceData.count());
131 memcpy(instanceData, fInstanceData.begin(), fInstanceData.count());
132 }
133}
134
135namespace {
136
137// Our round rect geometry consists of an inset octagon with solid coverage, surrounded by linear
138// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
139// edges. The Vertex struct tells the shader where to place its vertex within a normalized
140// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
141struct Vertex {
142 std::array<float, 4> fRadiiSelector;
143 std::array<float, 2> fCorner;
144 std::array<float, 2> fRadiusOutset;
145 std::array<float, 2> fAABloatDirection;
146 float fCoverage;
147 float fIsLinearCoverage;
Chris Dalton133944a2018-11-16 23:30:29 -0500148};
149
150// This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
151// of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
152// rectangles.
153static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
154
Chris Dalton133944a2018-11-16 23:30:29 -0500155static constexpr Vertex kVertexData[] = {
156 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700157 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
158 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500159
160 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700161 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
162 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500163
164 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700165 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
166 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500167
168 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700169 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
170 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500171
172
173 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700174 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
175 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500176
177 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700178 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
179 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500180
181 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700182 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
183 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500184
185 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700186 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
187 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500188
189
190 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700191 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
192 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
193 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
194 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
195 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
196 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500197
198 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700199 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
200 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
201 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
202 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
203 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
204 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500205
206 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700207 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
208 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
209 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
210 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
211 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
212 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500213
214 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700215 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
216 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
217 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
218 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
Chris Dalton2d07e862018-11-26 12:30:47 -0700219 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700220 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500221
222GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);
223
224static constexpr uint16_t kIndexData[] = {
225 // Inset octagon (solid coverage).
226 0, 1, 7,
227 1, 2, 7,
228 7, 2, 6,
229 2, 3, 6,
230 6, 3, 5,
231 3, 4, 5,
232
233 // AA borders (linear coverage).
234 0, 1, 8, 1, 9, 8,
235 2, 3, 10, 3, 11, 10,
236 4, 5, 12, 5, 13, 12,
237 6, 7, 14, 7, 15, 14,
238
239 // Top-left arc.
240 16, 17, 21,
241 17, 21, 18,
242 21, 18, 20,
243 18, 20, 19,
244
245 // Top-right arc.
246 22, 23, 27,
247 23, 27, 24,
248 27, 24, 26,
249 24, 26, 25,
250
251 // Bottom-right arc.
252 28, 29, 33,
253 29, 33, 30,
254 33, 30, 32,
255 30, 32, 31,
256
257 // Bottom-left arc.
258 34, 35, 39,
259 35, 39, 36,
260 39, 36, 38,
261 36, 38, 37};
262
263GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
264
265}
266
267class GrAAFillRRectOp::Processor : public GrGeometryProcessor {
268public:
269 Processor(Flags flags)
270 : GrGeometryProcessor(kGrAAFillRRectOp_Processor_ClassID)
271 , fFlags(flags) {
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700272 this->setVertexAttributes(kVertexAttribs, 3);
Brian Osman5105d682019-02-13 16:06:14 -0500273 fInSkew = { "skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType };
274 fInTranslate = { "translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType };
275 fInRadiiX = { "radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType };
276 fInRadiiY = { "radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType };
277 fInColor = MakeColorAttribute("color", (flags & Flags::kWideColor));
278 fInLocalRect = {"local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
279
280 this->setInstanceAttributes(&fInSkew, (flags & Flags::kHasLocalCoords) ? 6 : 5);
Chris Dalton133944a2018-11-16 23:30:29 -0500281 SkASSERT(this->vertexStride() == sizeof(Vertex));
282 }
283
284 const char* name() const override { return "GrAAFillRRectOp::Processor"; }
285
286 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
287 b->add32(static_cast<uint32_t>(fFlags));
288 }
289
290 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
291
292private:
293 static constexpr Attribute kVertexAttribs[] = {
294 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
295 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700296 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
Chris Dalton133944a2018-11-16 23:30:29 -0500297
Brian Osman5105d682019-02-13 16:06:14 -0500298 Attribute fInSkew;
299 Attribute fInTranslate;
300 Attribute fInRadiiX;
301 Attribute fInRadiiY;
302 Attribute fInColor;
303 Attribute fInLocalRect; // Conditional.
Chris Dalton133944a2018-11-16 23:30:29 -0500304
305 const Flags fFlags;
306
307 class Impl;
308};
309
310constexpr GrPrimitiveProcessor::Attribute GrAAFillRRectOp::Processor::kVertexAttribs[];
Chris Dalton133944a2018-11-16 23:30:29 -0500311
312class GrAAFillRRectOp::Processor::Impl : public GrGLSLGeometryProcessor {
313public:
314 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
315 const auto& proc = args.fGP.cast<Processor>();
316 bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives);
317
318 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
319 varyings->emitAttributes(proc);
Brian Osman5105d682019-02-13 16:06:14 -0500320 varyings->addPassThroughAttribute(proc.fInColor, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500321 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
322
323 // Emit the vertex shader.
324 GrGLSLVertexBuilder* v = args.fVertBuilder;
325
326 // Unpack vertex attribs.
327 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
328 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
329 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
330 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
331 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
332
333 // Find the amount to bloat each edge for AA (in source space).
334 v->codeAppend("float2 pixellength = inversesqrt("
335 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
336 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
337 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
338 "abs(normalized_axis_dirs.zw));");
339 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
340
341 // Identify our radii.
Mike Reedd3efa992018-11-28 13:13:15 +0000342 v->codeAppend("float4 radii_and_neighbors = radii_selector"
343 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
344 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
345 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500346
347 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
348 // The rrect is more narrow than an AA coverage ramp. We can't draw as-is
349 // or else opposite AA borders will overlap. Instead, fudge the size up to
350 // the width of a coverage ramp, and then reduce total coverage to make
351 // the rect appear more thin.
352 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
353 v->codeAppend( "coverage /= max(aa_bloatradius.x, 1) * max(aa_bloatradius.y, 1);");
354 // Set radii to zero to ensure we take the "linear coverage" codepath.
355 // (The "coverage" variable only has effect in the linear codepath.)
356 v->codeAppend( "radii = float2(0);");
357 v->codeAppend("}");
358
359 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.25))) {");
360 // The radii are very small. Demote this arc to a sharp 90 degree corner.
361 v->codeAppend( "radii = aa_bloatradius;");
362 // Snap octagon vertices to the corner of the bounding box.
363 v->codeAppend( "radius_outset = floor(abs(radius_outset)) * radius_outset;");
364 v->codeAppend( "is_linear_coverage = 1;");
365 v->codeAppend("} else {");
Mike Reedd3efa992018-11-28 13:13:15 +0000366 // Don't let radii get smaller than a pixel.
Chris Dalton133944a2018-11-16 23:30:29 -0500367 v->codeAppend( "radii = clamp(radii, pixellength, 2 - pixellength);");
Mike Reedd3efa992018-11-28 13:13:15 +0000368 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength, 2 - pixellength);");
369 // Don't let neighboring radii get closer together than 1/16 pixel.
370 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
371 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
372 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500373 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500374
375 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
376 // normalized [-1,-1,+1,+1] space.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700377 v->codeAppend("float2 aa_outset = aa_bloat_direction.xy * aa_bloatradius;");
378 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500379
380 // Emit transforms.
381 GrShaderVar localCoord("", kFloat2_GrSLType);
382 if (proc.fFlags & Flags::kHasLocalCoords) {
383 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
384 "local_rect.zw * (1 + vertexpos)) * .5;");
385 localCoord.set(kFloat2_GrSLType, "localcoord");
386 }
387 this->emitTransforms(v, varyings, args.fUniformHandler, localCoord,
388 args.fFPCoordTransformHandler);
389
390 // Transform to device space.
391 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
392 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
393 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
394
395 // Setup interpolants for coverage.
396 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
397 varyings->addVarying("arccoord", &arcCoord);
398 v->codeAppend("if (0 != is_linear_coverage) {");
399 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
400 // interpolate linear coverage across y.
401 v->codeAppendf( "%s.xy = float2(0, coverage);", arcCoord.vsOut());
402 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700403 // Find the normalized arc coordinates for our corner ellipse.
404 // (i.e., the coordinate system where x^2 + y^2 == 1).
405 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500406 // We are a corner piece: Interpolate the arc coordinates for coverage.
407 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
408 // instructs the fragment shader to use linear coverage).
409 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
410 if (!useHWDerivatives) {
411 // The gradient is order-1: Interpolate it across arccoord.zw.
412 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
413 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
414 }
415 v->codeAppend("}");
416
417 // Emit the fragment shader.
418 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
419
420 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
421 f->codeAppendf("half coverage;");
422 f->codeAppendf("if (0 == x_plus_1) {");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500423 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (i.e., linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500424 f->codeAppendf("} else {");
425 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
426 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
427 if (useHWDerivatives) {
428 f->codeAppendf("float fnwidth = fwidth(fn);");
429 } else {
430 // The gradient is interpolated across arccoord.zw.
431 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
432 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
433 }
Ethan Nicholase1f55022019-02-05 17:17:40 -0500434 f->codeAppendf( "half d = half(fn/fnwidth);");
Chris Dalton133944a2018-11-16 23:30:29 -0500435 f->codeAppendf( "coverage = clamp(.5 - d, 0, 1);");
436 f->codeAppendf("}");
437 f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage);
438 }
439
440 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
441 FPCoordTransformIter&& transformIter) override {
442 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
443 }
444};
445
446GrGLSLPrimitiveProcessor* GrAAFillRRectOp::Processor::createGLSLInstance(
447 const GrShaderCaps&) const {
448 return new Impl();
449}
450
451void GrAAFillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
452 if (!fInstanceBuffer) {
453 return; // Setup failed.
454 }
455
456 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
457
Brian Salomonae64c192019-02-05 09:41:37 -0500458 sk_sp<const GrBuffer> indexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
459 GrGpuBufferType::kIndex, sizeof(kIndexData), kIndexData, gIndexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500460 if (!indexBuffer) {
461 return;
462 }
463
464 GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
465
Brian Salomonae64c192019-02-05 09:41:37 -0500466 sk_sp<const GrBuffer> vertexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
467 GrGpuBufferType::kVertex, sizeof(kVertexData), kVertexData, gVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500468 if (!vertexBuffer) {
469 return;
470 }
471
472 Processor proc(fFlags);
473 SkASSERT(proc.instanceStride() == (size_t)fInstanceStride);
474
475 GrPipeline::InitArgs initArgs;
Chris Dalton133944a2018-11-16 23:30:29 -0500476 initArgs.fCaps = &flushState->caps();
477 initArgs.fResourceProvider = flushState->resourceProvider();
478 initArgs.fDstProxy = flushState->drawOpArgs().fDstProxy;
Chris Dalton8fa16252018-11-19 13:37:31 -0700479 auto clip = flushState->detachAppliedClip();
480 GrPipeline::FixedDynamicState fixedDynamicState(clip.scissorState().rect());
481 GrPipeline pipeline(initArgs, std::move(fProcessors), std::move(clip));
Chris Dalton133944a2018-11-16 23:30:29 -0500482
483 GrMesh mesh(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500484 mesh.setIndexedInstanced(std::move(indexBuffer), SK_ARRAY_COUNT(kIndexData), fInstanceBuffer,
Chris Dalton133944a2018-11-16 23:30:29 -0500485 fInstanceCount, fBaseInstance, GrPrimitiveRestart::kNo);
Brian Salomon12d22642019-01-29 14:38:50 -0500486 mesh.setVertexData(std::move(vertexBuffer));
Chris Dalton8fa16252018-11-19 13:37:31 -0700487 flushState->rtCommandBuffer()->draw(proc, pipeline, &fixedDynamicState, nullptr, &mesh, 1,
488 this->bounds());
Chris Dalton133944a2018-11-16 23:30:29 -0500489}
490
491// Will the given corner look good if we use HW derivatives?
492static bool can_use_hw_derivatives(const Sk2f& devScale, const Sk2f& cornerRadii) {
493 Sk2f devRadii = devScale * cornerRadii;
494 if (devRadii[1] < devRadii[0]) {
495 devRadii = SkNx_shuffle<1,0>(devRadii);
496 }
497 float minDevRadius = SkTMax(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
498 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
499 // This threshold was arrived at subjevtively on an NVIDIA chip.
500 return minDevRadius * minDevRadius * 5 > devRadii[1];
501}
502
503static bool can_use_hw_derivatives(const Sk2f& devScale, const SkVector& cornerRadii) {
504 return can_use_hw_derivatives(devScale, Sk2f::Load(&cornerRadii));
505}
506
507// Will the given round rect look good if we use HW derivatives?
508static bool can_use_hw_derivatives(const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
509 const SkRRect& rrect) {
510 if (!shaderCaps.shaderDerivativeSupport()) {
511 return false;
512 }
513
514 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
515 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
516 Sk2f devScale = (x*x + y*y).sqrt();
517 switch (rrect.getType()) {
518 case SkRRect::kEmpty_Type:
519 case SkRRect::kRect_Type:
520 return true;
521
522 case SkRRect::kOval_Type:
523 case SkRRect::kSimple_Type:
524 return can_use_hw_derivatives(devScale, rrect.getSimpleRadii());
525
526 case SkRRect::kNinePatch_Type: {
527 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
528 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
529 Sk2f minRadii = Sk2f::Min(r0, r1);
530 Sk2f maxRadii = Sk2f::Max(r0, r1);
531 return can_use_hw_derivatives(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
532 can_use_hw_derivatives(devScale, Sk2f(maxRadii[0], minRadii[1]));
533 }
534
535 case SkRRect::kComplex_Type: {
536 for (int i = 0; i < 4; ++i) {
537 auto corner = static_cast<SkRRect::Corner>(i);
538 if (!can_use_hw_derivatives(devScale, rrect.radii(corner))) {
539 return false;
540 }
541 }
542 return true;
543 }
544 }
545 SK_ABORT("Unreachable code.");
546 return false; // Add this return to keep GCC happy.
547}