1 /*
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 "src/gpu/ops/GrFillRRectOp.h"
9
10 #include "include/private/GrRecordingContext.h"
11 #include "src/core/SkRRectPriv.h"
12 #include "src/gpu/GrCaps.h"
13 #include "src/gpu/GrGpuCommandBuffer.h"
14 #include "src/gpu/GrMemoryPool.h"
15 #include "src/gpu/GrOpFlushState.h"
16 #include "src/gpu/GrRecordingContextPriv.h"
17 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
18 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
19 #include "src/gpu/glsl/GrGLSLVarying.h"
20 #include "src/gpu/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.
24 static bool can_use_hw_derivatives_with_coverage(
25 const GrShaderCaps&, const SkMatrix&, const SkRRect&);
26
Make(GrRecordingContext * ctx,GrAAType aaType,const SkMatrix & viewMatrix,const SkRRect & rrect,const GrCaps & caps,GrPaint && paint)27 std::unique_ptr<GrFillRRectOp> GrFillRRectOp::Make(
28 GrRecordingContext* ctx, GrAAType aaType, const SkMatrix& viewMatrix, const SkRRect& rrect,
29 const GrCaps& caps, GrPaint&& paint) {
30 if (!caps.instanceAttribSupport()) {
31 return nullptr;
32 }
33
34 Flags flags = Flags::kNone;
35 if (GrAAType::kCoverage == aaType) {
36 // TODO: Support perspective in a follow-on CL. This shouldn't be difficult, since we
37 // already use HW derivatives. The only trick will be adjusting the AA outset to account for
38 // perspective. (i.e., outset = 0.5 * z.)
39 if (viewMatrix.hasPerspective()) {
40 return nullptr;
41 }
42 if (can_use_hw_derivatives_with_coverage(*caps.shaderCaps(), viewMatrix, rrect)) {
43 // HW derivatives (more specifically, fwidth()) are consistently faster on all platforms
44 // in coverage mode. We use them as long as the approximation will be accurate enough.
45 flags |= Flags::kUseHWDerivatives;
46 }
47 } else {
48 if (GrAAType::kMSAA == aaType) {
49 if (!caps.sampleLocationsSupport() || !caps.shaderCaps()->sampleVariablesSupport()) {
50 return nullptr;
51 }
52 }
53 if (viewMatrix.hasPerspective()) {
54 // HW derivatives are consistently slower on all platforms in sample mask mode. We
55 // therefore only use them when there is perspective, since then we can't interpolate
56 // the symbolic screen-space gradient.
57 flags |= Flags::kUseHWDerivatives | Flags::kHasPerspective;
58 }
59 }
60
61 // Produce a matrix that draws the round rect from normalized [-1, -1, +1, +1] space.
62 float l = rrect.rect().left(), r = rrect.rect().right(),
63 t = rrect.rect().top(), b = rrect.rect().bottom();
64 SkMatrix m;
65 // Unmap the normalized rect [-1, -1, +1, +1] back to [l, t, r, b].
66 m.setScaleTranslate((r - l)/2, (b - t)/2, (l + r)/2, (t + b)/2);
67 // Map to device space.
68 m.postConcat(viewMatrix);
69
70 SkRect devBounds;
71 if (!(flags & Flags::kHasPerspective)) {
72 // Since m is an affine matrix that maps the rect [-1, -1, +1, +1] into the shape's
73 // device-space quad, it's quite simple to find the bounding rectangle:
74 devBounds = SkRect::MakeXYWH(m.getTranslateX(), m.getTranslateY(), 0, 0);
75 devBounds.outset(SkScalarAbs(m.getScaleX()) + SkScalarAbs(m.getSkewX()),
76 SkScalarAbs(m.getSkewY()) + SkScalarAbs(m.getScaleY()));
77 } else {
78 viewMatrix.mapRect(&devBounds, rrect.rect());
79 }
80
81 if (GrAAType::kMSAA == aaType && caps.preferTrianglesOverSampleMask()) {
82 // We are on a platform that prefers fine triangles instead of using the sample mask. See if
83 // the round rect is large enough that it will be faster for us to send it off to the
84 // default path renderer instead. The 200x200 threshold was arrived at using the
85 // "shapes_rrect" benchmark on an ARM Galaxy S9.
86 if (devBounds.height() * devBounds.width() > 200 * 200) {
87 return nullptr;
88 }
89 }
90
91 GrOpMemoryPool* pool = ctx->priv().opMemoryPool();
92 return pool->allocate<GrFillRRectOp>(aaType, rrect, flags, m, std::move(paint), devBounds);
93 }
94
GrFillRRectOp(GrAAType aaType,const SkRRect & rrect,Flags flags,const SkMatrix & totalShapeMatrix,GrPaint && paint,const SkRect & devBounds)95 GrFillRRectOp::GrFillRRectOp(
96 GrAAType aaType, const SkRRect& rrect, Flags flags,
97 const SkMatrix& totalShapeMatrix, GrPaint&& paint, const SkRect& devBounds)
98 : GrDrawOp(ClassID())
99 , fAAType(aaType)
100 , fOriginalColor(paint.getColor4f())
101 , fLocalRect(rrect.rect())
102 , fFlags(flags)
103 , fProcessors(std::move(paint)) {
104 SkASSERT((fFlags & Flags::kHasPerspective) == totalShapeMatrix.hasPerspective());
105 this->setBounds(devBounds, GrOp::HasAABloat::kYes, GrOp::IsZeroArea::kNo);
106
107 // Write the matrix attribs.
108 const SkMatrix& m = totalShapeMatrix;
109 if (!(fFlags & Flags::kHasPerspective)) {
110 // Affine 2D transformation (float2x2 plus float2 translate).
111 SkASSERT(!m.hasPerspective());
112 this->writeInstanceData(m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY());
113 this->writeInstanceData(m.getTranslateX(), m.getTranslateY());
114 } else {
115 // Perspective float3x3 transformation matrix.
116 SkASSERT(m.hasPerspective());
117 m.get9(this->appendInstanceData<float>(9));
118 }
119
120 // Convert the radii to [-1, -1, +1, +1] space and write their attribs.
121 Sk4f radiiX, radiiY;
122 Sk4f::Load2(SkRRectPriv::GetRadiiArray(rrect), &radiiX, &radiiY);
123 (radiiX * (2/rrect.width())).store(this->appendInstanceData<float>(4));
124 (radiiY * (2/rrect.height())).store(this->appendInstanceData<float>(4));
125
126 // We will write the color and local rect attribs during finalize().
127 }
128
finalize(const GrCaps & caps,const GrAppliedClip * clip,bool hasMixedSampledCoverage,GrClampType clampType)129 GrProcessorSet::Analysis GrFillRRectOp::finalize(
130 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
131 GrClampType clampType) {
132 SkASSERT(1 == fInstanceCount);
133
134 SkPMColor4f overrideColor;
135 const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
136
137 fOriginalColor, GrProcessorAnalysisCoverage::kSingleChannel, clip,
138 &GrUserStencilSettings::kUnused, hasMixedSampledCoverage, caps, clampType,
139 &overrideColor);
140
141 // Finish writing the instance attribs.
142 SkPMColor4f finalColor = analysis.inputColorIsOverridden() ? overrideColor : fOriginalColor;
143 if (!SkPMColor4fFitsInBytes(finalColor)) {
144 fFlags |= Flags::kWideColor;
145 uint32_t halfColor[2];
146 SkFloatToHalf_finite_ftz(Sk4f::Load(finalColor.vec())).store(&halfColor);
147 this->writeInstanceData(halfColor[0], halfColor[1]);
148 } else {
149 this->writeInstanceData(finalColor.toBytes_RGBA());
150 }
151
152 if (analysis.usesLocalCoords()) {
153 this->writeInstanceData(fLocalRect);
154 fFlags |= Flags::kHasLocalCoords;
155 }
156 fInstanceStride = fInstanceData.count();
157
158 return analysis;
159 }
160
onCombineIfPossible(GrOp * op,const GrCaps &)161 GrDrawOp::CombineResult GrFillRRectOp::onCombineIfPossible(GrOp* op, const GrCaps&) {
162 const auto& that = *op->cast<GrFillRRectOp>();
163 if (fFlags != that.fFlags || fProcessors != that.fProcessors ||
164 fInstanceData.count() > std::numeric_limits<int>::max() - that.fInstanceData.count()) {
165 return CombineResult::kCannotCombine;
166 }
167
168 fInstanceData.push_back_n(that.fInstanceData.count(), that.fInstanceData.begin());
169 fInstanceCount += that.fInstanceCount;
170 SkASSERT(fInstanceStride == that.fInstanceStride);
171 return CombineResult::kMerged;
172 }
173
onPrepare(GrOpFlushState * flushState)174 void GrFillRRectOp::onPrepare(GrOpFlushState* flushState) {
175 if (void* instanceData = flushState->makeVertexSpace(fInstanceStride, fInstanceCount,
176 &fInstanceBuffer, &fBaseInstance)) {
177 SkASSERT(fInstanceStride * fInstanceCount == fInstanceData.count());
178 memcpy(instanceData, fInstanceData.begin(), fInstanceData.count());
179 }
180 }
181
182 class GrFillRRectOp::Processor : public GrGeometryProcessor {
183 public:
Processor(GrAAType aaType,Flags flags)184 Processor(GrAAType aaType, Flags flags)
185 : GrGeometryProcessor(kGrFillRRectOp_Processor_ClassID)
186 , fAAType(aaType)
187 , fFlags(flags) {
188 int numVertexAttribs = (GrAAType::kCoverage == fAAType) ? 3 : 2;
189 this->setVertexAttributes(kVertexAttribs, numVertexAttribs);
190
191 if (!(flags & Flags::kHasPerspective)) {
192 // Affine 2D transformation (float2x2 plus float2 translate).
193 fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
194 fInstanceAttribs.emplace_back(
195 "translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
196 } else {
197 // Perspective float3x3 transformation matrix.
198 fInstanceAttribs.emplace_back("persp_x", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
199 fInstanceAttribs.emplace_back("persp_y", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
200 fInstanceAttribs.emplace_back("persp_z", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
201 }
202 fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
203 fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
204 fColorAttrib = &fInstanceAttribs.push_back(
205 MakeColorAttribute("color", (flags & Flags::kWideColor)));
206 if (fFlags & Flags::kHasLocalCoords) {
207 fInstanceAttribs.emplace_back(
208 "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
209 }
210 this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
211
212 if (GrAAType::kMSAA == fAAType) {
213 this->setWillUseCustomFeature(CustomFeatures::kSampleLocations);
214 }
215 }
216
name() const217 const char* name() const override { return "GrFillRRectOp::Processor"; }
218
getGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const219 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
220 b->add32(((uint32_t)fFlags << 16) | (uint32_t)fAAType);
221 }
222
223 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
224
225 private:
226 static constexpr Attribute kVertexAttribs[] = {
227 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
228 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
229 // Coverage only.
230 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
231
232 const GrAAType fAAType;
233 const Flags fFlags;
234
235 SkSTArray<6, Attribute> fInstanceAttribs;
236 const Attribute* fColorAttrib;
237
238 class CoverageImpl;
239 class MSAAImpl;
240 };
241
242 constexpr GrPrimitiveProcessor::Attribute GrFillRRectOp::Processor::kVertexAttribs[];
243
244 // Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
245 // coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
246 // edges. The Vertex struct tells the shader where to place its vertex within a normalized
247 // ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
248 struct CoverageVertex {
249 std::array<float, 4> fRadiiSelector;
250 std::array<float, 2> fCorner;
251 std::array<float, 2> fRadiusOutset;
252 std::array<float, 2> fAABloatDirection;
253 float fCoverage;
254 float fIsLinearCoverage;
255 };
256
257 // This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
258 // of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
259 // rectangles.
260 static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
261
262 static constexpr CoverageVertex kCoverageVertexData[] = {
263 // Left inset edge.
264 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
265 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
266
267 // Top inset edge.
268 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
269 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
270
271 // Right inset edge.
272 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
273 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
274
275 // Bottom inset edge.
276 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
277 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
278
279
280 // Left outset edge.
281 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
282 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
283
284 // Top outset edge.
285 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
286 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
287
288 // Right outset edge.
289 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
290 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
291
292 // Bottom outset edge.
293 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
294 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
295
296
297 // Top-left corner.
298 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
299 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
300 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
301 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
302 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
303 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
304
305 // Top-right corner.
306 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
307 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
308 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
309 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
310 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
311 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
312
313 // Bottom-right corner.
314 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
315 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
316 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
317 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
318 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
319 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
320
321 // Bottom-left corner.
322 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
323 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
324 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
325 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
326 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
327 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
328
329 GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
330
331 static constexpr uint16_t kCoverageIndexData[] = {
332 // Inset octagon (solid coverage).
333 0, 1, 7,
334 1, 2, 7,
335 7, 2, 6,
336 2, 3, 6,
337 6, 3, 5,
338 3, 4, 5,
339
340 // AA borders (linear coverage).
341 0, 1, 8, 1, 9, 8,
342 2, 3, 10, 3, 11, 10,
343 4, 5, 12, 5, 13, 12,
344 6, 7, 14, 7, 15, 14,
345
346 // Top-left arc.
347 16, 17, 21,
348 17, 21, 18,
349 21, 18, 20,
350 18, 20, 19,
351
352 // Top-right arc.
353 22, 23, 27,
354 23, 27, 24,
355 27, 24, 26,
356 24, 26, 25,
357
358 // Bottom-right arc.
359 28, 29, 33,
360 29, 33, 30,
361 33, 30, 32,
362 30, 32, 31,
363
364 // Bottom-left arc.
365 34, 35, 39,
366 35, 39, 36,
367 39, 36, 38,
368 36, 38, 37};
369
370 GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
371
372 class GrFillRRectOp::Processor::CoverageImpl : public GrGLSLGeometryProcessor {
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)373 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
374 const auto& proc = args.fGP.cast<Processor>();
375 bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives);
376
377 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
378
379 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
380 varyings->emitAttributes(proc);
381 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
382 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
383
384 // Emit the vertex shader.
385 GrGLSLVertexBuilder* v = args.fVertBuilder;
386
387 // Unpack vertex attribs.
388 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
389 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
390 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
391 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
392 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
393
394 // Find the amount to bloat each edge for AA (in source space).
395 v->codeAppend("float2 pixellength = inversesqrt("
396 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
397 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
398 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
399 "abs(normalized_axis_dirs.zw));");
400 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
401
402 // Identify our radii.
403 v->codeAppend("float4 radii_and_neighbors = radii_selector"
404 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
405 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
406 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
407
408 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
409 // The rrect is more narrow than an AA coverage ramp. We can't draw as-is
410 // or else opposite AA borders will overlap. Instead, fudge the size up to
411 // the width of a coverage ramp, and then reduce total coverage to make
412 // the rect appear more thin.
413 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
414 v->codeAppend( "coverage /= max(aa_bloatradius.x, 1) * max(aa_bloatradius.y, 1);");
415 // Set radii to zero to ensure we take the "linear coverage" codepath.
416 // (The "coverage" variable only has effect in the linear codepath.)
417 v->codeAppend( "radii = float2(0);");
418 v->codeAppend("}");
419
420 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.25))) {");
421 // The radii are very small. Demote this arc to a sharp 90 degree corner.
422 v->codeAppend( "radii = aa_bloatradius;");
423 // Snap octagon vertices to the corner of the bounding box.
424 v->codeAppend( "radius_outset = floor(abs(radius_outset)) * radius_outset;");
425 v->codeAppend( "is_linear_coverage = 1;");
426 v->codeAppend("} else {");
427 // Don't let radii get smaller than a pixel.
428 v->codeAppend( "radii = clamp(radii, pixellength, 2 - pixellength);");
429 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength, 2 - pixellength);");
430 // Don't let neighboring radii get closer together than 1/16 pixel.
431 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
432 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
433 v->codeAppend( "radii -= extra_pad * .5;");
434 v->codeAppend("}");
435
436 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
437 // normalized [-1,-1,+1,+1] space.
438 v->codeAppend("float2 aa_outset = aa_bloat_direction.xy * aa_bloatradius;");
439 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
440
441 // Emit transforms.
442 GrShaderVar localCoord("", kFloat2_GrSLType);
443 if (proc.fFlags & Flags::kHasLocalCoords) {
444 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
445 "local_rect.zw * (1 + vertexpos)) * .5;");
446 localCoord.set(kFloat2_GrSLType, "localcoord");
447 }
448 this->emitTransforms(v, varyings, args.fUniformHandler, localCoord,
449 args.fFPCoordTransformHandler);
450
451 // Transform to device space.
452 SkASSERT(!(proc.fFlags & Flags::kHasPerspective));
453 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
454 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
455 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
456
457 // Setup interpolants for coverage.
458 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
459 varyings->addVarying("arccoord", &arcCoord);
460 v->codeAppend("if (0 != is_linear_coverage) {");
461 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
462 // interpolate linear coverage across y.
463 v->codeAppendf( "%s.xy = float2(0, coverage);", arcCoord.vsOut());
464 v->codeAppend("} else {");
465 // Find the normalized arc coordinates for our corner ellipse.
466 // (i.e., the coordinate system where x^2 + y^2 == 1).
467 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
468 // We are a corner piece: Interpolate the arc coordinates for coverage.
469 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
470 // instructs the fragment shader to use linear coverage).
471 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
472 if (!useHWDerivatives) {
473 // The gradient is order-1: Interpolate it across arccoord.zw.
474 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
475 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
476 }
477 v->codeAppend("}");
478
479 // Emit the fragment shader.
480 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
481
482 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
483 f->codeAppendf("half coverage;");
484 f->codeAppendf("if (0 == x_plus_1) {");
485 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
486 f->codeAppendf("} else {");
487 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
488 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
489 if (useHWDerivatives) {
490 f->codeAppendf("float fnwidth = fwidth(fn);");
491 } else {
492 // The gradient is interpolated across arccoord.zw.
493 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
494 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
495 }
496 f->codeAppendf( "half d = half(fn/fnwidth);");
497 f->codeAppendf( "coverage = clamp(.5 - d, 0, 1);");
498 f->codeAppendf("}");
499 f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage);
500 }
501
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor &,FPCoordTransformIter && transformIter)502 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
503 FPCoordTransformIter&& transformIter) override {
504 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
505 }
506 };
507
508 // Our MSAA geometry consists of an inset octagon with full sample mask coverage, circumscribed
509 // by a larger octagon that modifies the sample mask for the arc at each corresponding corner.
510 struct MSAAVertex {
511 std::array<float, 4> fRadiiSelector;
512 std::array<float, 2> fCorner;
513 std::array<float, 2> fRadiusOutset;
514 };
515
516 static constexpr MSAAVertex kMSAAVertexData[] = {
517 // Left edge. (Negative radii selector indicates this is not an arc section.)
518 {{{0,0,0,-1}}, {{-1,+1}}, {{0,-1}}},
519 {{{-1,0,0,0}}, {{-1,-1}}, {{0,+1}}},
520
521 // Top edge.
522 {{{-1,0,0,0}}, {{-1,-1}}, {{+1,0}}},
523 {{{0,-1,0,0}}, {{+1,-1}}, {{-1,0}}},
524
525 // Right edge.
526 {{{0,-1,0,0}}, {{+1,-1}}, {{0,+1}}},
527 {{{0,0,-1,0}}, {{+1,+1}}, {{0,-1}}},
528
529 // Bottom edge.
530 {{{0,0,-1,0}}, {{+1,+1}}, {{-1,0}}},
531 {{{0,0,0,-1}}, {{-1,+1}}, {{+1,0}}},
532
533 // Top-left corner.
534 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}},
535 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}},
536 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}},
537 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}},
538
539 // Top-right corner.
540 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}},
541 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}},
542 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}},
543 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}},
544
545 // Bottom-right corner.
546 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}},
547 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}},
548 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}},
549 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}},
550
551 // Bottom-left corner.
552 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}},
553 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}},
554 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}},
555 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}}};
556
557 GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
558
559 static constexpr uint16_t kMSAAIndexData[] = {
560 // Inset octagon. (Full sample mask.)
561 0, 1, 2,
562 0, 2, 3,
563 0, 3, 6,
564 3, 4, 5,
565 3, 5, 6,
566 6, 7, 0,
567
568 // Top-left arc. (Sample mask is set to the arc.)
569 8, 9, 10,
570 9, 11, 10,
571
572 // Top-right arc.
573 12, 13, 14,
574 13, 15, 14,
575
576 // Bottom-right arc.
577 16, 17, 18,
578 17, 19, 18,
579
580 // Bottom-left arc.
581 20, 21, 22,
582 21, 23, 22};
583
584 GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
585
586 class GrFillRRectOp::Processor::MSAAImpl : public GrGLSLGeometryProcessor {
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)587 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
588 const auto& proc = args.fGP.cast<Processor>();
589 bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives);
590 bool hasPerspective = (proc.fFlags & Flags::kHasPerspective);
591 bool hasLocalCoords = (proc.fFlags & Flags::kHasLocalCoords);
592 SkASSERT(useHWDerivatives == hasPerspective);
593
594 SkASSERT(proc.vertexStride() == sizeof(MSAAVertex));
595
596 // Emit the vertex shader.
597 GrGLSLVertexBuilder* v = args.fVertBuilder;
598
599 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
600 varyings->emitAttributes(proc);
601 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
602 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
603
604 // Unpack vertex attribs.
605 v->codeAppendf("float2 corner = corner_and_radius_outsets.xy;");
606 v->codeAppendf("float2 radius_outset = corner_and_radius_outsets.zw;");
607
608 // Identify our radii.
609 v->codeAppend("float2 radii;");
610 v->codeAppend("radii.x = dot(radii_selector, radii_x);");
611 v->codeAppend("radii.y = dot(radii_selector, radii_y);");
612 v->codeAppendf("bool is_arc_section = (radii.x > 0);");
613 v->codeAppendf("radii = abs(radii);");
614
615 // Find our vertex position, adjusted for radii. Our rect is drawn in normalized
616 // [-1,-1,+1,+1] space.
617 v->codeAppend("float2 vertexpos = corner + radius_outset * radii;");
618
619 // Emit transforms.
620 GrShaderVar localCoord("", kFloat2_GrSLType);
621 if (hasLocalCoords) {
622 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
623 "local_rect.zw * (1 + vertexpos)) * .5;");
624 localCoord.set(kFloat2_GrSLType, "localcoord");
625 }
626 this->emitTransforms(v, varyings, args.fUniformHandler, localCoord,
627 args.fFPCoordTransformHandler);
628
629 // Transform to device space.
630 if (!hasPerspective) {
631 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
632 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
633 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
634 } else {
635 v->codeAppend("float3x3 persp_matrix = float3x3(persp_x, persp_y, persp_z);");
636 v->codeAppend("float3 devcoord = float3(vertexpos, 1) * persp_matrix;");
637 gpArgs->fPositionVar.set(kFloat3_GrSLType, "devcoord");
638 }
639
640 // Determine normalized arc coordinates for the implicit function.
641 GrGLSLVarying arcCoord((useHWDerivatives) ? kFloat2_GrSLType : kFloat4_GrSLType);
642 varyings->addVarying("arccoord", &arcCoord);
643 v->codeAppendf("if (is_arc_section) {");
644 v->codeAppendf( "%s.xy = 1 - abs(radius_outset);", arcCoord.vsOut());
645 if (!useHWDerivatives) {
646 // The gradient is order-1: Interpolate it across arccoord.zw.
647 // This doesn't work with perspective.
648 SkASSERT(!hasPerspective);
649 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
650 v->codeAppendf("%s.zw = derivatives * (%s.xy/radii * corner * 2);",
651 arcCoord.vsOut(), arcCoord.vsOut());
652 }
653 v->codeAppendf("} else {");
654 if (useHWDerivatives) {
655 v->codeAppendf("%s = float2(0);", arcCoord.vsOut());
656 } else {
657 v->codeAppendf("%s = float4(0);", arcCoord.vsOut());
658 }
659 v->codeAppendf("}");
660
661 // Emit the fragment shader.
662 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
663
664 f->codeAppendf("%s = half4(1);", args.fOutputCoverage);
665
666 // If x,y == 0, then we are drawing a triangle that does not track an arc.
667 f->codeAppendf("if (float2(0) != %s.xy) {", arcCoord.fsIn());
668 f->codeAppendf( "float fn = dot(%s.xy, %s.xy) - 1;", arcCoord.fsIn(), arcCoord.fsIn());
669 if (GrAAType::kMSAA == proc.fAAType) {
670 using ScopeFlags = GrGLSLFPFragmentBuilder::ScopeFlags;
671 if (!useHWDerivatives) {
672 f->codeAppendf("float2 grad = %s.zw;", arcCoord.fsIn());
673 f->applyFnToMultisampleMask("fn", "grad", ScopeFlags::kInsidePerPrimitiveBranch);
674 } else {
675 f->applyFnToMultisampleMask("fn", nullptr, ScopeFlags::kInsidePerPrimitiveBranch);
676 }
677 } else {
678 f->codeAppendf("if (fn > 0) {");
679 f->codeAppendf( "%s = half4(0);", args.fOutputCoverage);
680 f->codeAppendf("}");
681 }
682 f->codeAppendf("}");
683 }
684
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor &,FPCoordTransformIter && transformIter)685 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
686 FPCoordTransformIter&& transformIter) override {
687 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
688 }
689 };
690
createGLSLInstance(const GrShaderCaps &) const691 GrGLSLPrimitiveProcessor* GrFillRRectOp::Processor::createGLSLInstance(
692 const GrShaderCaps&) const {
693 if (GrAAType::kCoverage != fAAType) {
694 return new MSAAImpl();
695 }
696 return new CoverageImpl();
697 }
698
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)699 void GrFillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
700 if (!fInstanceBuffer) {
701 return; // Setup failed.
702 }
703
704 sk_sp<const GrBuffer> indexBuffer, vertexBuffer;
705 int indexCount;
706
707 if (GrAAType::kCoverage == fAAType) {
708 GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
709
710 indexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
711 GrGpuBufferType::kIndex, sizeof(kCoverageIndexData), kCoverageIndexData,
712 gCoverageIndexBufferKey);
713
714 GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
715
716 vertexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
717 GrGpuBufferType::kVertex, sizeof(kCoverageVertexData), kCoverageVertexData,
718 gCoverageVertexBufferKey);
719
720 indexCount = SK_ARRAY_COUNT(kCoverageIndexData);
721 } else {
722 GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
723
724 indexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
725 GrGpuBufferType::kIndex, sizeof(kMSAAIndexData), kMSAAIndexData,
726 gMSAAIndexBufferKey);
727
728 GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
729
730 vertexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
731 GrGpuBufferType::kVertex, sizeof(kMSAAVertexData), kMSAAVertexData,
732 gMSAAVertexBufferKey);
733
734 indexCount = SK_ARRAY_COUNT(kMSAAIndexData);
735 }
736
737 if (!indexBuffer || !vertexBuffer) {
738 return;
739 }
740
741 Processor* proc = flushState->allocator()->make<Processor>(fAAType, fFlags);
742 SkASSERT(proc->instanceStride() == (size_t)fInstanceStride);
743
744 GrPipeline::InitArgs initArgs;
745 if (GrAAType::kMSAA == fAAType) {
746 initArgs.fInputFlags = GrPipeline::InputFlags::kHWAntialias;
747 }
748 initArgs.fCaps = &flushState->caps();
749 initArgs.fDstProxy = flushState->drawOpArgs().fDstProxy;
750 initArgs.fOutputSwizzle = flushState->drawOpArgs().fOutputSwizzle;
751 auto clip = flushState->detachAppliedClip();
752 GrPipeline::FixedDynamicState* fixedDynamicState =
753 flushState->allocator()->make<GrPipeline::FixedDynamicState>(clip.scissorState().rect());
754 GrPipeline* pipeline = flushState->allocator()->make<GrPipeline>(initArgs,
755 std::move(fProcessors),
756 std::move(clip));
757
758 GrMesh* mesh = flushState->allocator()->make<GrMesh>(GrPrimitiveType::kTriangles);
759 mesh->setIndexedInstanced(
760 std::move(indexBuffer), indexCount, fInstanceBuffer, fInstanceCount, fBaseInstance,
761 GrPrimitiveRestart::kNo);
762 mesh->setVertexData(std::move(vertexBuffer));
763 flushState->rtCommandBuffer()->draw(
764 *proc, *pipeline, fixedDynamicState, nullptr, mesh, 1, this->bounds());
765 }
766
767 // Will the given corner look good if we use HW derivatives?
can_use_hw_derivatives_with_coverage(const Sk2f & devScale,const Sk2f & cornerRadii)768 static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
769 Sk2f devRadii = devScale * cornerRadii;
770 if (devRadii[1] < devRadii[0]) {
771 devRadii = SkNx_shuffle<1,0>(devRadii);
772 }
773 float minDevRadius = SkTMax(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
774 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
775 // This threshold was arrived at subjevtively on an NVIDIA chip.
776 return minDevRadius * minDevRadius * 5 > devRadii[1];
777 }
778
can_use_hw_derivatives_with_coverage(const Sk2f & devScale,const SkVector & cornerRadii)779 static bool can_use_hw_derivatives_with_coverage(
780 const Sk2f& devScale, const SkVector& cornerRadii) {
781 return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
782 }
783
784 // Will the given round rect look good if we use HW derivatives?
can_use_hw_derivatives_with_coverage(const GrShaderCaps & shaderCaps,const SkMatrix & viewMatrix,const SkRRect & rrect)785 static bool can_use_hw_derivatives_with_coverage(
786 const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
787 if (!shaderCaps.shaderDerivativeSupport()) {
788 return false;
789 }
790
791 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
792 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
793 Sk2f devScale = (x*x + y*y).sqrt();
794 switch (rrect.getType()) {
795 case SkRRect::kEmpty_Type:
796 case SkRRect::kRect_Type:
797 return true;
798
799 case SkRRect::kOval_Type:
800 case SkRRect::kSimple_Type:
801 return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
802
803 case SkRRect::kNinePatch_Type: {
804 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
805 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
806 Sk2f minRadii = Sk2f::Min(r0, r1);
807 Sk2f maxRadii = Sk2f::Max(r0, r1);
808 return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
809 can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
810 }
811
812 case SkRRect::kComplex_Type: {
813 for (int i = 0; i < 4; ++i) {
814 auto corner = static_cast<SkRRect::Corner>(i);
815 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
816 return false;
817 }
818 }
819 return true;
820 }
821 }
822 SK_ABORT("Invalid round rect type.");
823 }
824