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