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