blob: cb09ce3e52019d49f6a8a6e5efb2aa687708cbd8 [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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "src/gpu/GrMemoryPool.h"
14#include "src/gpu/GrOpFlushState.h"
Greg Daniel2d41d0d2019-08-26 11:08:51 -040015#include "src/gpu/GrOpsRenderPass.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050016#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());
Greg Daniel5faf4742019-10-01 15:14:44 -0400105 this->setBounds(devBounds, GrOp::HasAABloat::kYes, GrOp::IsHairline::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 Dalton0dffbab2019-03-27 13:08:50 -0600174class GrFillRRectOp::Processor : public GrGeometryProcessor {
175public:
176 Processor(GrAAType aaType, Flags flags)
177 : GrGeometryProcessor(kGrFillRRectOp_Processor_ClassID)
178 , fAAType(aaType)
179 , fFlags(flags) {
180 int numVertexAttribs = (GrAAType::kCoverage == fAAType) ? 3 : 2;
181 this->setVertexAttributes(kVertexAttribs, numVertexAttribs);
Chris Dalton133944a2018-11-16 23:30:29 -0500182
Chris Dalton0dffbab2019-03-27 13:08:50 -0600183 if (!(flags & Flags::kHasPerspective)) {
184 // Affine 2D transformation (float2x2 plus float2 translate).
185 fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
186 fInstanceAttribs.emplace_back(
187 "translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
188 } else {
189 // Perspective float3x3 transformation matrix.
190 fInstanceAttribs.emplace_back("persp_x", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
191 fInstanceAttribs.emplace_back("persp_y", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
192 fInstanceAttribs.emplace_back("persp_z", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
193 }
194 fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
195 fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
196 fColorAttrib = &fInstanceAttribs.push_back(
197 MakeColorAttribute("color", (flags & Flags::kWideColor)));
198 if (fFlags & Flags::kHasLocalCoords) {
199 fInstanceAttribs.emplace_back(
200 "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
201 }
202 this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
203
204 if (GrAAType::kMSAA == fAAType) {
205 this->setWillUseCustomFeature(CustomFeatures::kSampleLocations);
206 }
207 }
208
209 const char* name() const override { return "GrFillRRectOp::Processor"; }
210
211 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
212 b->add32(((uint32_t)fFlags << 16) | (uint32_t)fAAType);
213 }
214
215 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
216
217private:
218 static constexpr Attribute kVertexAttribs[] = {
219 {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
220 {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
221 // Coverage only.
222 {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
223
224 const GrAAType fAAType;
225 const Flags fFlags;
226
227 SkSTArray<6, Attribute> fInstanceAttribs;
228 const Attribute* fColorAttrib;
229
230 class CoverageImpl;
231 class MSAAImpl;
232};
233
234constexpr GrPrimitiveProcessor::Attribute GrFillRRectOp::Processor::kVertexAttribs[];
235
236// Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
Chris Dalton133944a2018-11-16 23:30:29 -0500237// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
238// edges. The Vertex struct tells the shader where to place its vertex within a normalized
239// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600240struct CoverageVertex {
Chris Dalton133944a2018-11-16 23:30:29 -0500241 std::array<float, 4> fRadiiSelector;
242 std::array<float, 2> fCorner;
243 std::array<float, 2> fRadiusOutset;
244 std::array<float, 2> fAABloatDirection;
245 float fCoverage;
246 float fIsLinearCoverage;
Chris Dalton133944a2018-11-16 23:30:29 -0500247};
248
249// This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices
250// of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than
251// rectangles.
252static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
253
Chris Dalton0dffbab2019-03-27 13:08:50 -0600254static constexpr CoverageVertex kCoverageVertexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500255 // Left inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700256 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
257 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500258
259 // Top inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700260 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1},
261 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500262
263 // Right inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700264 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1},
265 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500266
267 // Bottom inset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700268 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1},
269 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500270
271
272 // Left outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700273 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1},
274 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500275
276 // Top outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700277 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1},
278 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500279
280 // Right outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700281 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1},
282 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500283
284 // Bottom outset edge.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700285 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1},
286 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1},
Chris Dalton133944a2018-11-16 23:30:29 -0500287
288
289 // Top-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700290 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0},
291 {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0},
292 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0},
293 {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0},
294 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0},
295 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500296
297 // Top-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700298 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0},
299 {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0},
300 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0},
301 {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0},
302 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0},
303 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500304
305 // Bottom-right corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700306 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0},
307 {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0},
308 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0},
309 {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0},
310 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0},
311 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0},
Chris Dalton133944a2018-11-16 23:30:29 -0500312
313 // Bottom-left corner.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700314 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0},
315 {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0},
316 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0},
317 {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0},
Chris Dalton2d07e862018-11-26 12:30:47 -0700318 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700319 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
Chris Dalton133944a2018-11-16 23:30:29 -0500320
Chris Dalton0dffbab2019-03-27 13:08:50 -0600321GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500322
Chris Dalton0dffbab2019-03-27 13:08:50 -0600323static constexpr uint16_t kCoverageIndexData[] = {
Chris Dalton133944a2018-11-16 23:30:29 -0500324 // Inset octagon (solid coverage).
325 0, 1, 7,
326 1, 2, 7,
327 7, 2, 6,
328 2, 3, 6,
329 6, 3, 5,
330 3, 4, 5,
331
332 // AA borders (linear coverage).
333 0, 1, 8, 1, 9, 8,
334 2, 3, 10, 3, 11, 10,
335 4, 5, 12, 5, 13, 12,
336 6, 7, 14, 7, 15, 14,
337
338 // Top-left arc.
339 16, 17, 21,
340 17, 21, 18,
341 21, 18, 20,
342 18, 20, 19,
343
344 // Top-right arc.
345 22, 23, 27,
346 23, 27, 24,
347 27, 24, 26,
348 24, 26, 25,
349
350 // Bottom-right arc.
351 28, 29, 33,
352 29, 33, 30,
353 33, 30, 32,
354 30, 32, 31,
355
356 // Bottom-left arc.
357 34, 35, 39,
358 35, 39, 36,
359 39, 36, 38,
360 36, 38, 37};
361
Chris Dalton0dffbab2019-03-27 13:08:50 -0600362GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
Chris Dalton133944a2018-11-16 23:30:29 -0500363
Greg Danielf793de12019-09-05 13:23:23 -0400364
365// Our MSAA geometry consists of an inset octagon with full sample mask coverage, circumscribed
366// by a larger octagon that modifies the sample mask for the arc at each corresponding corner.
367struct MSAAVertex {
368 std::array<float, 4> fRadiiSelector;
369 std::array<float, 2> fCorner;
370 std::array<float, 2> fRadiusOutset;
371};
372
373static constexpr MSAAVertex kMSAAVertexData[] = {
374 // Left edge. (Negative radii selector indicates this is not an arc section.)
375 {{{0,0,0,-1}}, {{-1,+1}}, {{0,-1}}},
376 {{{-1,0,0,0}}, {{-1,-1}}, {{0,+1}}},
377
378 // Top edge.
379 {{{-1,0,0,0}}, {{-1,-1}}, {{+1,0}}},
380 {{{0,-1,0,0}}, {{+1,-1}}, {{-1,0}}},
381
382 // Right edge.
383 {{{0,-1,0,0}}, {{+1,-1}}, {{0,+1}}},
384 {{{0,0,-1,0}}, {{+1,+1}}, {{0,-1}}},
385
386 // Bottom edge.
387 {{{0,0,-1,0}}, {{+1,+1}}, {{-1,0}}},
388 {{{0,0,0,-1}}, {{-1,+1}}, {{+1,0}}},
389
390 // Top-left corner.
391 {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}},
392 {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}},
393 {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}},
394 {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}},
395
396 // Top-right corner.
397 {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}},
398 {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}},
399 {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}},
400 {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}},
401
402 // Bottom-right corner.
403 {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}},
404 {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}},
405 {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}},
406 {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}},
407
408 // Bottom-left corner.
409 {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}},
410 {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}},
411 {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}},
412 {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}}};
413
414GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
415
416static constexpr uint16_t kMSAAIndexData[] = {
417 // Inset octagon. (Full sample mask.)
418 0, 1, 2,
419 0, 2, 3,
420 0, 3, 6,
421 3, 4, 5,
422 3, 5, 6,
423 6, 7, 0,
424
425 // Top-left arc. (Sample mask is set to the arc.)
426 8, 9, 10,
427 9, 11, 10,
428
429 // Top-right arc.
430 12, 13, 14,
431 13, 15, 14,
432
433 // Bottom-right arc.
434 16, 17, 18,
435 17, 19, 18,
436
437 // Bottom-left arc.
438 20, 21, 22,
439 21, 23, 22};
440
441GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
442
443void GrFillRRectOp::onPrepare(GrOpFlushState* flushState) {
444 if (void* instanceData = flushState->makeVertexSpace(fInstanceStride, fInstanceCount,
445 &fInstanceBuffer, &fBaseInstance)) {
446 SkASSERT(fInstanceStride * fInstanceCount == fInstanceData.count());
447 memcpy(instanceData, fInstanceData.begin(), fInstanceData.count());
448 }
449
450 if (GrAAType::kCoverage == fAAType) {
451 GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
452
453 fIndexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
454 GrGpuBufferType::kIndex, sizeof(kCoverageIndexData), kCoverageIndexData,
455 gCoverageIndexBufferKey);
456
457 GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
458
459 fVertexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
460 GrGpuBufferType::kVertex, sizeof(kCoverageVertexData), kCoverageVertexData,
461 gCoverageVertexBufferKey);
462
463 fIndexCount = SK_ARRAY_COUNT(kCoverageIndexData);
464 } else {
465 GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
466
467 fIndexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
468 GrGpuBufferType::kIndex, sizeof(kMSAAIndexData), kMSAAIndexData,
469 gMSAAIndexBufferKey);
470
471 GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
472
473 fVertexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
474 GrGpuBufferType::kVertex, sizeof(kMSAAVertexData), kMSAAVertexData,
475 gMSAAVertexBufferKey);
476
477 fIndexCount = SK_ARRAY_COUNT(kMSAAIndexData);
478 }
479}
480
Chris Dalton0dffbab2019-03-27 13:08:50 -0600481class GrFillRRectOp::Processor::CoverageImpl : public GrGLSLGeometryProcessor {
Chris Dalton133944a2018-11-16 23:30:29 -0500482 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
483 const auto& proc = args.fGP.cast<Processor>();
484 bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives);
485
Chris Dalton0dffbab2019-03-27 13:08:50 -0600486 SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
487
Chris Dalton133944a2018-11-16 23:30:29 -0500488 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
489 varyings->emitAttributes(proc);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600490 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
Chris Dalton133944a2018-11-16 23:30:29 -0500491 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
492
493 // Emit the vertex shader.
494 GrGLSLVertexBuilder* v = args.fVertBuilder;
495
496 // Unpack vertex attribs.
497 v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
498 v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
499 v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
500 v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
501 v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");
502
503 // Find the amount to bloat each edge for AA (in source space).
504 v->codeAppend("float2 pixellength = inversesqrt("
505 "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
506 v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
507 v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
508 "abs(normalized_axis_dirs.zw));");
509 v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");
510
511 // Identify our radii.
Mike Reedd3efa992018-11-28 13:13:15 +0000512 v->codeAppend("float4 radii_and_neighbors = radii_selector"
513 "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
514 v->codeAppend("float2 radii = radii_and_neighbors.xy;");
515 v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");
Chris Dalton133944a2018-11-16 23:30:29 -0500516
517 v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
518 // The rrect is more narrow than an AA coverage ramp. We can't draw as-is
519 // or else opposite AA borders will overlap. Instead, fudge the size up to
520 // the width of a coverage ramp, and then reduce total coverage to make
521 // the rect appear more thin.
522 v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
523 v->codeAppend( "coverage /= max(aa_bloatradius.x, 1) * max(aa_bloatradius.y, 1);");
524 // Set radii to zero to ensure we take the "linear coverage" codepath.
525 // (The "coverage" variable only has effect in the linear codepath.)
526 v->codeAppend( "radii = float2(0);");
527 v->codeAppend("}");
528
529 v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.25))) {");
530 // The radii are very small. Demote this arc to a sharp 90 degree corner.
531 v->codeAppend( "radii = aa_bloatradius;");
532 // Snap octagon vertices to the corner of the bounding box.
533 v->codeAppend( "radius_outset = floor(abs(radius_outset)) * radius_outset;");
534 v->codeAppend( "is_linear_coverage = 1;");
535 v->codeAppend("} else {");
Mike Reedd3efa992018-11-28 13:13:15 +0000536 // Don't let radii get smaller than a pixel.
Chris Dalton133944a2018-11-16 23:30:29 -0500537 v->codeAppend( "radii = clamp(radii, pixellength, 2 - pixellength);");
Mike Reedd3efa992018-11-28 13:13:15 +0000538 v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength, 2 - pixellength);");
539 // Don't let neighboring radii get closer together than 1/16 pixel.
540 v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;");
541 v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
542 v->codeAppend( "radii -= extra_pad * .5;");
Chris Dalton133944a2018-11-16 23:30:29 -0500543 v->codeAppend("}");
Chris Dalton133944a2018-11-16 23:30:29 -0500544
545 // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
546 // normalized [-1,-1,+1,+1] space.
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700547 v->codeAppend("float2 aa_outset = aa_bloat_direction.xy * aa_bloatradius;");
548 v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");
Chris Dalton133944a2018-11-16 23:30:29 -0500549
550 // Emit transforms.
551 GrShaderVar localCoord("", kFloat2_GrSLType);
552 if (proc.fFlags & Flags::kHasLocalCoords) {
553 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
554 "local_rect.zw * (1 + vertexpos)) * .5;");
555 localCoord.set(kFloat2_GrSLType, "localcoord");
556 }
557 this->emitTransforms(v, varyings, args.fUniformHandler, localCoord,
558 args.fFPCoordTransformHandler);
559
560 // Transform to device space.
Chris Dalton0dffbab2019-03-27 13:08:50 -0600561 SkASSERT(!(proc.fFlags & Flags::kHasPerspective));
Chris Dalton133944a2018-11-16 23:30:29 -0500562 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
563 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
564 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
565
566 // Setup interpolants for coverage.
567 GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
568 varyings->addVarying("arccoord", &arcCoord);
569 v->codeAppend("if (0 != is_linear_coverage) {");
570 // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
571 // interpolate linear coverage across y.
572 v->codeAppendf( "%s.xy = float2(0, coverage);", arcCoord.vsOut());
573 v->codeAppend("} else {");
Chris Daltonaa71f0a2018-11-21 18:14:45 -0700574 // Find the normalized arc coordinates for our corner ellipse.
575 // (i.e., the coordinate system where x^2 + y^2 == 1).
576 v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
Chris Dalton133944a2018-11-16 23:30:29 -0500577 // We are a corner piece: Interpolate the arc coordinates for coverage.
578 // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
579 // instructs the fragment shader to use linear coverage).
580 v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
581 if (!useHWDerivatives) {
582 // The gradient is order-1: Interpolate it across arccoord.zw.
583 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
584 v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
585 }
586 v->codeAppend("}");
587
588 // Emit the fragment shader.
589 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
590
591 f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
592 f->codeAppendf("half coverage;");
593 f->codeAppendf("if (0 == x_plus_1) {");
Chris Dalton0dffbab2019-03-27 13:08:50 -0600594 f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
Chris Dalton133944a2018-11-16 23:30:29 -0500595 f->codeAppendf("} else {");
596 f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
597 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
598 if (useHWDerivatives) {
599 f->codeAppendf("float fnwidth = fwidth(fn);");
600 } else {
601 // The gradient is interpolated across arccoord.zw.
602 f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
603 f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
604 }
Ethan Nicholase1f55022019-02-05 17:17:40 -0500605 f->codeAppendf( "half d = half(fn/fnwidth);");
Chris Dalton133944a2018-11-16 23:30:29 -0500606 f->codeAppendf( "coverage = clamp(.5 - d, 0, 1);");
607 f->codeAppendf("}");
608 f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage);
609 }
610
611 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
612 FPCoordTransformIter&& transformIter) override {
613 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
614 }
615};
616
Chris Dalton0dffbab2019-03-27 13:08:50 -0600617
618class GrFillRRectOp::Processor::MSAAImpl : public GrGLSLGeometryProcessor {
619 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
620 const auto& proc = args.fGP.cast<Processor>();
621 bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives);
622 bool hasPerspective = (proc.fFlags & Flags::kHasPerspective);
623 bool hasLocalCoords = (proc.fFlags & Flags::kHasLocalCoords);
624 SkASSERT(useHWDerivatives == hasPerspective);
625
626 SkASSERT(proc.vertexStride() == sizeof(MSAAVertex));
627
628 // Emit the vertex shader.
629 GrGLSLVertexBuilder* v = args.fVertBuilder;
630
631 GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
632 varyings->emitAttributes(proc);
633 varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
634 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
635
636 // Unpack vertex attribs.
637 v->codeAppendf("float2 corner = corner_and_radius_outsets.xy;");
638 v->codeAppendf("float2 radius_outset = corner_and_radius_outsets.zw;");
639
640 // Identify our radii.
641 v->codeAppend("float2 radii;");
642 v->codeAppend("radii.x = dot(radii_selector, radii_x);");
643 v->codeAppend("radii.y = dot(radii_selector, radii_y);");
644 v->codeAppendf("bool is_arc_section = (radii.x > 0);");
645 v->codeAppendf("radii = abs(radii);");
646
647 // Find our vertex position, adjusted for radii. Our rect is drawn in normalized
648 // [-1,-1,+1,+1] space.
649 v->codeAppend("float2 vertexpos = corner + radius_outset * radii;");
650
651 // Emit transforms.
652 GrShaderVar localCoord("", kFloat2_GrSLType);
653 if (hasLocalCoords) {
654 v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
655 "local_rect.zw * (1 + vertexpos)) * .5;");
656 localCoord.set(kFloat2_GrSLType, "localcoord");
657 }
658 this->emitTransforms(v, varyings, args.fUniformHandler, localCoord,
659 args.fFPCoordTransformHandler);
660
661 // Transform to device space.
662 if (!hasPerspective) {
663 v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
664 v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
665 gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
666 } else {
667 v->codeAppend("float3x3 persp_matrix = float3x3(persp_x, persp_y, persp_z);");
668 v->codeAppend("float3 devcoord = float3(vertexpos, 1) * persp_matrix;");
669 gpArgs->fPositionVar.set(kFloat3_GrSLType, "devcoord");
670 }
671
672 // Determine normalized arc coordinates for the implicit function.
673 GrGLSLVarying arcCoord((useHWDerivatives) ? kFloat2_GrSLType : kFloat4_GrSLType);
674 varyings->addVarying("arccoord", &arcCoord);
675 v->codeAppendf("if (is_arc_section) {");
676 v->codeAppendf( "%s.xy = 1 - abs(radius_outset);", arcCoord.vsOut());
677 if (!useHWDerivatives) {
678 // The gradient is order-1: Interpolate it across arccoord.zw.
679 // This doesn't work with perspective.
680 SkASSERT(!hasPerspective);
681 v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
682 v->codeAppendf("%s.zw = derivatives * (%s.xy/radii * corner * 2);",
683 arcCoord.vsOut(), arcCoord.vsOut());
684 }
685 v->codeAppendf("} else {");
686 if (useHWDerivatives) {
687 v->codeAppendf("%s = float2(0);", arcCoord.vsOut());
688 } else {
689 v->codeAppendf("%s = float4(0);", arcCoord.vsOut());
690 }
691 v->codeAppendf("}");
692
693 // Emit the fragment shader.
694 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
695
696 f->codeAppendf("%s = half4(1);", args.fOutputCoverage);
697
698 // If x,y == 0, then we are drawing a triangle that does not track an arc.
699 f->codeAppendf("if (float2(0) != %s.xy) {", arcCoord.fsIn());
700 f->codeAppendf( "float fn = dot(%s.xy, %s.xy) - 1;", arcCoord.fsIn(), arcCoord.fsIn());
701 if (GrAAType::kMSAA == proc.fAAType) {
702 using ScopeFlags = GrGLSLFPFragmentBuilder::ScopeFlags;
703 if (!useHWDerivatives) {
704 f->codeAppendf("float2 grad = %s.zw;", arcCoord.fsIn());
705 f->applyFnToMultisampleMask("fn", "grad", ScopeFlags::kInsidePerPrimitiveBranch);
706 } else {
707 f->applyFnToMultisampleMask("fn", nullptr, ScopeFlags::kInsidePerPrimitiveBranch);
708 }
709 } else {
710 f->codeAppendf("if (fn > 0) {");
711 f->codeAppendf( "%s = half4(0);", args.fOutputCoverage);
712 f->codeAppendf("}");
713 }
714 f->codeAppendf("}");
715 }
716
717 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
718 FPCoordTransformIter&& transformIter) override {
719 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
720 }
721};
722
Chris Dalton82eb9e72019-03-21 14:26:39 -0600723GrGLSLPrimitiveProcessor* GrFillRRectOp::Processor::createGLSLInstance(
Chris Dalton133944a2018-11-16 23:30:29 -0500724 const GrShaderCaps&) const {
Chris Dalton0dffbab2019-03-27 13:08:50 -0600725 if (GrAAType::kCoverage != fAAType) {
726 return new MSAAImpl();
727 }
728 return new CoverageImpl();
Chris Dalton133944a2018-11-16 23:30:29 -0500729}
730
Chris Dalton82eb9e72019-03-21 14:26:39 -0600731void GrFillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Greg Danielf793de12019-09-05 13:23:23 -0400732 if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
Chris Dalton133944a2018-11-16 23:30:29 -0500733 return; // Setup failed.
734 }
735
Greg Daniel0b002e22019-08-12 13:25:36 -0400736 Processor* proc = flushState->allocator()->make<Processor>(fAAType, fFlags);
737 SkASSERT(proc->instanceStride() == (size_t)fInstanceStride);
Chris Dalton133944a2018-11-16 23:30:29 -0500738
739 GrPipeline::InitArgs initArgs;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600740 if (GrAAType::kMSAA == fAAType) {
Chris Daltonbaa1b352019-04-03 12:03:00 -0600741 initArgs.fInputFlags = GrPipeline::InputFlags::kHWAntialias;
Chris Dalton0dffbab2019-03-27 13:08:50 -0600742 }
Chris Dalton133944a2018-11-16 23:30:29 -0500743 initArgs.fCaps = &flushState->caps();
Chris Dalton133944a2018-11-16 23:30:29 -0500744 initArgs.fDstProxy = flushState->drawOpArgs().fDstProxy;
Greg Daniel2c3398d2019-06-19 11:58:01 -0400745 initArgs.fOutputSwizzle = flushState->drawOpArgs().fOutputSwizzle;
Chris Dalton8fa16252018-11-19 13:37:31 -0700746 auto clip = flushState->detachAppliedClip();
Greg Daniel0b002e22019-08-12 13:25:36 -0400747 GrPipeline::FixedDynamicState* fixedDynamicState =
748 flushState->allocator()->make<GrPipeline::FixedDynamicState>(clip.scissorState().rect());
749 GrPipeline* pipeline = flushState->allocator()->make<GrPipeline>(initArgs,
750 std::move(fProcessors),
751 std::move(clip));
Chris Dalton133944a2018-11-16 23:30:29 -0500752
Greg Daniel0b002e22019-08-12 13:25:36 -0400753 GrMesh* mesh = flushState->allocator()->make<GrMesh>(GrPrimitiveType::kTriangles);
754 mesh->setIndexedInstanced(
Greg Danielf793de12019-09-05 13:23:23 -0400755 std::move(fIndexBuffer), fIndexCount, std::move(fInstanceBuffer), fInstanceCount,
756 fBaseInstance, GrPrimitiveRestart::kNo);
757 mesh->setVertexData(std::move(fVertexBuffer));
Greg Daniel2d41d0d2019-08-26 11:08:51 -0400758 flushState->opsRenderPass()->draw(
Greg Daniel0b002e22019-08-12 13:25:36 -0400759 *proc, *pipeline, fixedDynamicState, nullptr, mesh, 1, this->bounds());
Greg Danielf793de12019-09-05 13:23:23 -0400760 fIndexCount = 0;
Chris Dalton133944a2018-11-16 23:30:29 -0500761}
762
763// Will the given corner look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600764static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
Chris Dalton133944a2018-11-16 23:30:29 -0500765 Sk2f devRadii = devScale * cornerRadii;
766 if (devRadii[1] < devRadii[0]) {
767 devRadii = SkNx_shuffle<1,0>(devRadii);
768 }
769 float minDevRadius = SkTMax(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1.
770 // Is the gradient smooth enough for this corner look ok if we use hardware derivatives?
771 // This threshold was arrived at subjevtively on an NVIDIA chip.
772 return minDevRadius * minDevRadius * 5 > devRadii[1];
773}
774
Chris Dalton0dffbab2019-03-27 13:08:50 -0600775static bool can_use_hw_derivatives_with_coverage(
776 const Sk2f& devScale, const SkVector& cornerRadii) {
777 return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
Chris Dalton133944a2018-11-16 23:30:29 -0500778}
779
780// Will the given round rect look good if we use HW derivatives?
Chris Dalton0dffbab2019-03-27 13:08:50 -0600781static bool can_use_hw_derivatives_with_coverage(
782 const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
Chris Dalton133944a2018-11-16 23:30:29 -0500783 if (!shaderCaps.shaderDerivativeSupport()) {
784 return false;
785 }
786
787 Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX());
788 Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY());
789 Sk2f devScale = (x*x + y*y).sqrt();
790 switch (rrect.getType()) {
791 case SkRRect::kEmpty_Type:
792 case SkRRect::kRect_Type:
793 return true;
794
795 case SkRRect::kOval_Type:
796 case SkRRect::kSimple_Type:
Chris Dalton0dffbab2019-03-27 13:08:50 -0600797 return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
Chris Dalton133944a2018-11-16 23:30:29 -0500798
799 case SkRRect::kNinePatch_Type: {
800 Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
801 Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
802 Sk2f minRadii = Sk2f::Min(r0, r1);
803 Sk2f maxRadii = Sk2f::Max(r0, r1);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600804 return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
805 can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
Chris Dalton133944a2018-11-16 23:30:29 -0500806 }
807
808 case SkRRect::kComplex_Type: {
809 for (int i = 0; i < 4; ++i) {
810 auto corner = static_cast<SkRRect::Corner>(i);
Chris Dalton0dffbab2019-03-27 13:08:50 -0600811 if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
Chris Dalton133944a2018-11-16 23:30:29 -0500812 return false;
813 }
814 }
815 return true;
816 }
817 }
Chris Dalton0dffbab2019-03-27 13:08:50 -0600818 SK_ABORT("Invalid round rect type.");
Chris Dalton133944a2018-11-16 23:30:29 -0500819}