blob: e5e18b99ce0bfb03b69fe416f30104062f6a332f [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/gpu/ops/GrFillRRectOp.h"
Chris Dalton133944a2018-11-16 23:30:29 -05009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#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"
Chris Dalton133944a2018-11-16 23:30:29 -050021
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.
Chris Dalton0dffbab2019-03-27 13:08:50 -060024static bool can_use_hw_derivatives_with_coverage(
25 const GrShaderCaps&, const SkMatrix&, const SkRRect&);
Chris Dalton133944a2018-11-16 23:30:29 -050026
Chris Dalton82eb9e72019-03-21 14:26:39 -060027std::unique_ptr<GrFillRRectOp> GrFillRRectOp::Make(
Chris Dalton0dffbab2019-03-27 13:08:50 -060028 GrRecordingContext* ctx, GrAAType aaType, const SkMatrix& viewMatrix, const SkRRect& rrect,
Robert Phillipsb97da532019-02-12 15:24:12 -050029 const GrCaps& caps, GrPaint&& paint) {
Chris Dalton133944a2018-11-16 23:30:29 -050030 if (!caps.instanceAttribSupport()) {
31 return nullptr;
32 }
33
Chris Dalton0dffbab2019-03-27 13:08:50 -060034 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 }
Chris Dalton133944a2018-11-16 23:30:29 -050059 }
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
Chris Dalton0dffbab2019-03-27 13:08:50 -060070 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
95GrFillRRectOp::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);
Chris Dalton133944a2018-11-16 23:30:29 -0500106
107 // Write the matrix attribs.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600108 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 }
Chris Dalton133944a2018-11-16 23:30:29 -0500119
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);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600123 (radiiX * (2/rrect.width())).store(this->appendInstanceData<float>(4));
124 (radiiY * (2/rrect.height())).store(this->appendInstanceData<float>(4));
Chris Dalton133944a2018-11-16 23:30:29 -0500125
126 // We will write the color and local rect attribs during finalize().
127}
128
Chris Dalton6ce447a2019-06-23 18:07:38 -0600129GrProcessorSet::Analysis GrFillRRectOp::finalize(
130 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
131 GrClampType clampType) {
Chris Dalton133944a2018-11-16 23:30:29 -0500132 SkASSERT(1 == fInstanceCount);
133
134 SkPMColor4f overrideColor;
135 const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
Chris Daltonb8fff0d2019-03-05 10:11:58 -0700136
137 fOriginalColor, GrProcessorAnalysisCoverage::kSingleChannel, clip,
Chris Dalton6ce447a2019-06-23 18:07:38 -0600138 &GrUserStencilSettings::kUnused, hasMixedSampledCoverage, caps, clampType,
139 &overrideColor);
Chris Dalton133944a2018-11-16 23:30:29 -0500140
141 // Finish writing the instance attribs.
Brian Osman5105d682019-02-13 16:06:14 -0500142 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
Chris Dalton133944a2018-11-16 23:30:29 -0500152 if (analysis.usesLocalCoords()) {
153 this->writeInstanceData(fLocalRect);
154 fFlags |= Flags::kHasLocalCoords;
155 }
156 fInstanceStride = fInstanceData.count();
157
Chris Dalton4b62aed2019-01-15 11:53:00 -0700158 return analysis;
Chris Dalton133944a2018-11-16 23:30:29 -0500159}
160
Chris Dalton82eb9e72019-03-21 14:26:39 -0600161GrDrawOp::CombineResult GrFillRRectOp::onCombineIfPossible(GrOp* op, const GrCaps&) {
162 const auto& that = *op->cast<GrFillRRectOp>();
Chris Dalton133944a2018-11-16 23:30:29 -0500163 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
Chris Dalton82eb9e72019-03-21 14:26:39 -0600174void GrFillRRectOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton133944a2018-11-16 23:30:29 -0500175 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
Chris Dalton0dffbab2019-03-27 13:08:50 -0600182class GrFillRRectOp::Processor : public GrGeometryProcessor {
183public:
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);
Chris Dalton133944a2018-11-16 23:30:29 -0500190
Chris Dalton0dffbab2019-03-27 13:08:50 -0600191 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
217 const char* name() const override { return "GrFillRRectOp::Processor"; }
218
219 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
225private:
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
242constexpr GrPrimitiveProcessor::Attribute GrFillRRectOp::Processor::kVertexAttribs[];
243
244// Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
Chris Dalton133944a2018-11-16 23:30:29 -0500245// 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.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600248struct CoverageVertex {
Chris Dalton133944a2018-11-16 23:30:29 -0500249 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;
Chris Dalton133944a2018-11-16 23:30:29 -0500255};
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.
260static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
261
Chris Dalton0dffbab2019-03-27 13:08:50 -0600262static constexpr CoverageVertex kCoverageVertexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500263 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700264 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500266
267 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700268 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500270
271 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700272 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500274
275 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700276 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500278
279
280 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700281 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500283
284 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700285 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500287
288 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700289 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500291
292 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700293 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500295
296
297 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700298 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500304
305 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700306 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500312
313 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700314 {{{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},
Chris Dalton133944a2018-11-16 23:30:29 -0500320
321 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700322 {{{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},
Chris Dalton2d07e862018-11-26 12:30:47 -0700326 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700327 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500328
Chris Dalton0dffbab2019-03-27 13:08:50 -0600329GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500330
Chris Dalton0dffbab2019-03-27 13:08:50 -0600331static constexpr uint16_t kCoverageIndexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500332 // 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
Chris Dalton0dffbab2019-03-27 13:08:50 -0600370GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500371
Chris Dalton0dffbab2019-03-27 13:08:50 -0600372class GrFillRRectOp::Processor::CoverageImpl : public GrGLSLGeometryProcessor {
Chris Dalton133944a2018-11-16 23:30:29 -0500373 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
374 const auto& proc = args.fGP.cast<Processor>();
375 bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives);
376
Chris Dalton0dffbab2019-03-27 13:08:50 -0600377 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
378
Chris Dalton133944a2018-11-16 23:30:29 -0500379 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
380 varyings->emitAttributes(proc);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600381 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500382 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.
Mike Reedd3efa992018-11-28 13:13:15 +0000403 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;");
Chris Dalton133944a2018-11-16 23:30:29 -0500407
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 {");
Mike Reedd3efa992018-11-28 13:13:15 +0000427 // Don't let radii get smaller than a pixel.
Chris Dalton133944a2018-11-16 23:30:29 -0500428 v->codeAppend( "radii = clamp(radii, pixellength, 2 - pixellength);");
Mike Reedd3efa992018-11-28 13:13:15 +0000429 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;");
Chris Dalton133944a2018-11-16 23:30:29 -0500434 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500435
436 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
437 // normalized [-1,-1,+1,+1] space.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700438 v->codeAppend("float2 aa_outset = aa_bloat_direction.xy * aa_bloatradius;");
439 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500440
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.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600452 SkASSERT(!(proc.fFlags & Flags::kHasPerspective));
Chris Dalton133944a2018-11-16 23:30:29 -0500453 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 {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700465 // 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;");
Chris Dalton133944a2018-11-16 23:30:29 -0500468 // 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) {");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600485 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500486 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 }
Ethan Nicholase1f55022019-02-05 17:17:40 -0500496 f->codeAppendf( "half d = half(fn/fnwidth);");
Chris Dalton133944a2018-11-16 23:30:29 -0500497 f->codeAppendf( "coverage = clamp(.5 - d, 0, 1);");
498 f->codeAppendf("}");
499 f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage);
500 }
501
502 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
503 FPCoordTransformIter&& transformIter) override {
504 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
505 }
506};
507
Chris Dalton0dffbab2019-03-27 13:08:50 -0600508// 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.
510struct MSAAVertex {
511 std::array<float, 4> fRadiiSelector;
512 std::array<float, 2> fCorner;
513 std::array<float, 2> fRadiusOutset;
514};
515
516static 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
557GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
558
559static 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
584GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
585
586class GrFillRRectOp::Processor::MSAAImpl : public GrGLSLGeometryProcessor {
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
685 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
686 FPCoordTransformIter&& transformIter) override {
687 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
688 }
689};
690
Chris Dalton82eb9e72019-03-21 14:26:39 -0600691GrGLSLPrimitiveProcessor* GrFillRRectOp::Processor::createGLSLInstance(
Chris Dalton133944a2018-11-16 23:30:29 -0500692 const GrShaderCaps&) const {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600693 if (GrAAType::kCoverage != fAAType) {
694 return new MSAAImpl();
695 }
696 return new CoverageImpl();
Chris Dalton133944a2018-11-16 23:30:29 -0500697}
698
Chris Dalton82eb9e72019-03-21 14:26:39 -0600699void GrFillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Dalton133944a2018-11-16 23:30:29 -0500700 if (!fInstanceBuffer) {
701 return; // Setup failed.
702 }
703
Chris Dalton0dffbab2019-03-27 13:08:50 -0600704 sk_sp<const GrBuffer> indexBuffer, vertexBuffer;
705 int indexCount;
Chris Dalton133944a2018-11-16 23:30:29 -0500706
Chris Dalton0dffbab2019-03-27 13:08:50 -0600707 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) {
Chris Dalton133944a2018-11-16 23:30:29 -0500738 return;
739 }
740
Chris Dalton0dffbab2019-03-27 13:08:50 -0600741 Processor proc(fAAType, fFlags);
Chris Dalton133944a2018-11-16 23:30:29 -0500742 SkASSERT(proc.instanceStride() == (size_t)fInstanceStride);
743
744 GrPipeline::InitArgs initArgs;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600745 if (GrAAType::kMSAA == fAAType) {
Chris Daltonbaa1b352019-04-03 12:03:00 -0600746 initArgs.fInputFlags = GrPipeline::InputFlags::kHWAntialias;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600747 }
Chris Dalton133944a2018-11-16 23:30:29 -0500748 initArgs.fCaps = &flushState->caps();
Chris Dalton133944a2018-11-16 23:30:29 -0500749 initArgs.fDstProxy = flushState->drawOpArgs().fDstProxy;
Greg Daniel2c3398d2019-06-19 11:58:01 -0400750 initArgs.fOutputSwizzle = flushState->drawOpArgs().fOutputSwizzle;
Chris Dalton8fa16252018-11-19 13:37:31 -0700751 auto clip = flushState->detachAppliedClip();
752 GrPipeline::FixedDynamicState fixedDynamicState(clip.scissorState().rect());
753 GrPipeline pipeline(initArgs, std::move(fProcessors), std::move(clip));
Chris Dalton133944a2018-11-16 23:30:29 -0500754
755 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600756 mesh.setIndexedInstanced(
757 std::move(indexBuffer), indexCount, fInstanceBuffer, fInstanceCount, fBaseInstance,
758 GrPrimitiveRestart::kNo);
Brian Salomon12d22642019-01-29 14:38:50 -0500759 mesh.setVertexData(std::move(vertexBuffer));
Chris Dalton0dffbab2019-03-27 13:08:50 -0600760 flushState->rtCommandBuffer()->draw(
761 proc, pipeline, &fixedDynamicState, nullptr, &mesh, 1, this->bounds());
Chris Dalton133944a2018-11-16 23:30:29 -0500762}
763
764// Will the given corner look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600765static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
Chris Dalton133944a2018-11-16 23:30:29 -0500766 Sk2f devRadii = devScale * cornerRadii;
767 if (devRadii[1] < devRadii[0]) {
768 devRadii = SkNx_shuffle<1,0>(devRadii);
769 }
770 float minDevRadius = SkTMax(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
771 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
772 // This threshold was arrived at subjevtively on an NVIDIA chip.
773 return minDevRadius * minDevRadius * 5 > devRadii[1];
774}
775
Chris Dalton0dffbab2019-03-27 13:08:50 -0600776static bool can_use_hw_derivatives_with_coverage(
777 const Sk2f& devScale, const SkVector& cornerRadii) {
778 return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
Chris Dalton133944a2018-11-16 23:30:29 -0500779}
780
781// Will the given round rect look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600782static bool can_use_hw_derivatives_with_coverage(
783 const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
Chris Dalton133944a2018-11-16 23:30:29 -0500784 if (!shaderCaps.shaderDerivativeSupport()) {
785 return false;
786 }
787
788 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
789 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
790 Sk2f devScale = (x*x + y*y).sqrt();
791 switch (rrect.getType()) {
792 case SkRRect::kEmpty_Type:
793 case SkRRect::kRect_Type:
794 return true;
795
796 case SkRRect::kOval_Type:
797 case SkRRect::kSimple_Type:
Chris Dalton0dffbab2019-03-27 13:08:50 -0600798 return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
Chris Dalton133944a2018-11-16 23:30:29 -0500799
800 case SkRRect::kNinePatch_Type: {
801 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
802 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
803 Sk2f minRadii = Sk2f::Min(r0, r1);
804 Sk2f maxRadii = Sk2f::Max(r0, r1);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600805 return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
806 can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
Chris Dalton133944a2018-11-16 23:30:29 -0500807 }
808
809 case SkRRect::kComplex_Type: {
810 for (int i = 0; i < 4; ++i) {
811 auto corner = static_cast<SkRRect::Corner>(i);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600812 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
Chris Dalton133944a2018-11-16 23:30:29 -0500813 return false;
814 }
815 }
816 return true;
817 }
818 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600819 SK_ABORT("Invalid round rect type.");
Chris Dalton133944a2018-11-16 23:30:29 -0500820 return false; // Add this return to keep GCC happy.
821}