blob: 1ac80e6f53cb234f163b41ca5b9cfbcb4423a91c [file] [log] [blame]
bsalomon@google.com30085192011-08-19 15:42:31 +00001/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrDefaultPathRenderer.h"
Robert Phillipsbe9aff22019-02-15 11:33:22 -05009
10#include "GrCaps.h"
joshualitt5478d422014-11-14 16:00:38 -080011#include "GrDefaultGeoProcFactory.h"
Brian Salomon5ec9def2016-12-20 15:34:05 -050012#include "GrDrawOpTest.h"
Michael Ludwig72ab3462018-12-10 12:43:36 -050013#include "GrFillRectOp.h"
csmartdalton02fa32c2016-08-19 13:29:27 -070014#include "GrFixedClip.h"
egdaniel0e1853c2016-03-17 11:35:45 -070015#include "GrMesh.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050016#include "GrOpFlushState.h"
bsalomon@google.com30085192011-08-19 15:42:31 +000017#include "GrPathUtils.h"
Brian Salomon653f42f2018-07-10 10:07:31 -040018#include "GrShape.h"
Brian Salomonee3e0ba2017-07-13 16:40:46 -040019#include "GrSimpleMeshDrawOpHelper.h"
Brian Salomon653f42f2018-07-10 10:07:31 -040020#include "GrStyle.h"
Robert Phillips7c525e62018-06-12 10:11:12 -040021#include "GrSurfaceContextPriv.h"
egdanielaf18a092015-01-05 10:22:28 -080022#include "SkGeometry.h"
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000023#include "SkString.h"
sugoi@google.com5f74cf82012-12-17 21:16:45 +000024#include "SkStrokeRec.h"
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +000025#include "SkTLazy.h"
commit-bot@chromium.org933e65d2014-03-20 20:00:24 +000026#include "SkTraceEvent.h"
Brian Salomon89527432016-12-16 09:52:16 -050027#include "ops/GrMeshDrawOp.h"
joshualitt74417822015-08-07 11:42:16 -070028
Brian Salomon15b25092017-05-08 11:10:53 -040029GrDefaultPathRenderer::GrDefaultPathRenderer() {
bsalomon@google.com289533a2011-10-27 12:34:25 +000030}
31
bsalomon@google.com30085192011-08-19 15:42:31 +000032////////////////////////////////////////////////////////////////////////////////
33// Helpers for drawPath
34
bsalomon@google.com30085192011-08-19 15:42:31 +000035#define STENCIL_OFF 0 // Always disable stencil (even when needed)
36
bsalomon8acedde2016-06-24 10:42:16 -070037static inline bool single_pass_shape(const GrShape& shape) {
bsalomon@google.com30085192011-08-19 15:42:31 +000038#if STENCIL_OFF
39 return true;
40#else
bsalomonee432412016-06-27 07:18:18 -070041 // Inverse fill is always two pass.
42 if (shape.inverseFilled()) {
43 return false;
44 }
45 // This path renderer only accepts simple fill paths or stroke paths that are either hairline
46 // or have a stroke width small enough to treat as hairline. Hairline paths are always single
47 // pass. Filled paths are single pass if they're convex.
48 if (shape.style().isSimpleFill()) {
bsalomon8acedde2016-06-24 10:42:16 -070049 return shape.knownToBeConvex();
bsalomon@google.com30085192011-08-19 15:42:31 +000050 }
bsalomonee432412016-06-27 07:18:18 -070051 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +000052#endif
53}
54
joshualitt9853cce2014-11-17 14:22:48 -080055GrPathRenderer::StencilSupport
bsalomon8acedde2016-06-24 10:42:16 -070056GrDefaultPathRenderer::onGetStencilSupport(const GrShape& shape) const {
57 if (single_pass_shape(shape)) {
bsalomon@google.com45a15f52012-12-10 19:10:17 +000058 return GrPathRenderer::kNoRestriction_StencilSupport;
59 } else {
60 return GrPathRenderer::kStencilOnly_StencilSupport;
61 }
bsalomon@google.com30085192011-08-19 15:42:31 +000062}
63
Brian Salomonee3e0ba2017-07-13 16:40:46 -040064namespace {
65
Brian Osman49b7b6f2017-06-20 14:43:58 -040066class PathGeoBuilder {
67public:
Brian Salomon763abf02018-05-01 18:49:38 +000068 PathGeoBuilder(GrPrimitiveType primitiveType, GrMeshDrawOp::Target* target,
Chris Dalton07cdcfc92019-02-26 11:13:22 -070069 sk_sp<const GrGeometryProcessor> geometryProcessor)
Brian Salomon7eae3e02018-08-07 14:02:38 +000070 : fPrimitiveType(primitiveType)
Brian Osman49b7b6f2017-06-20 14:43:58 -040071 , fTarget(target)
72 , fVertexStride(sizeof(SkPoint))
Brian Salomon7eae3e02018-08-07 14:02:38 +000073 , fGeometryProcessor(std::move(geometryProcessor))
Brian Osman49b7b6f2017-06-20 14:43:58 -040074 , fFirstIndex(0)
75 , fIndicesInChunk(0)
76 , fIndices(nullptr) {
77 this->allocNewBuffers();
Brian Osmaneb86b702017-06-07 11:38:31 -040078 }
79
Brian Osman49b7b6f2017-06-20 14:43:58 -040080 ~PathGeoBuilder() {
Brian Osman3902d402017-06-20 15:36:31 -040081 this->emitMeshAndPutBackReserve();
Brian Osman49b7b6f2017-06-20 14:43:58 -040082 }
83
84 /**
85 * Path verbs
86 */
87 void moveTo(const SkPoint& p) {
88 needSpace(1);
89
90 fSubpathIndexStart = this->currentIndex();
91 *(fCurVert++) = p;
92 }
93
94 void addLine(const SkPoint& p) {
95 needSpace(1, this->indexScale());
96
Brian Salomon763abf02018-05-01 18:49:38 +000097 if (this->isIndexed()) {
Brian Osman49b7b6f2017-06-20 14:43:58 -040098 uint16_t prevIdx = this->currentIndex() - 1;
99 appendCountourEdgeIndices(prevIdx);
100 }
101 *(fCurVert++) = p;
102 }
103
104 void addQuad(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
105 this->needSpace(GrPathUtils::kMaxPointsPerCurve,
106 GrPathUtils::kMaxPointsPerCurve * this->indexScale());
107
108 // First pt of quad is the pt we ended on in previous step
109 uint16_t firstQPtIdx = this->currentIndex() - 1;
110 uint16_t numPts = (uint16_t)GrPathUtils::generateQuadraticPoints(
111 pts[0], pts[1], pts[2], srcSpaceTolSqd, &fCurVert,
112 GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
Brian Salomon763abf02018-05-01 18:49:38 +0000113 if (this->isIndexed()) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400114 for (uint16_t i = 0; i < numPts; ++i) {
115 appendCountourEdgeIndices(firstQPtIdx + i);
116 }
egdanielaf18a092015-01-05 10:22:28 -0800117 }
118 }
Brian Osman49b7b6f2017-06-20 14:43:58 -0400119
120 void addConic(SkScalar weight, const SkPoint pts[], SkScalar srcSpaceTolSqd,
121 SkScalar srcSpaceTol) {
122 SkAutoConicToQuads converter;
123 const SkPoint* quadPts = converter.computeQuads(pts, weight, srcSpaceTol);
124 for (int i = 0; i < converter.countQuads(); ++i) {
125 this->addQuad(quadPts + i * 2, srcSpaceTolSqd, srcSpaceTol);
126 }
127 }
128
129 void addCubic(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
130 this->needSpace(GrPathUtils::kMaxPointsPerCurve,
131 GrPathUtils::kMaxPointsPerCurve * this->indexScale());
132
133 // First pt of cubic is the pt we ended on in previous step
134 uint16_t firstCPtIdx = this->currentIndex() - 1;
135 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
136 pts[0], pts[1], pts[2], pts[3], srcSpaceTolSqd, &fCurVert,
137 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
Brian Salomon763abf02018-05-01 18:49:38 +0000138 if (this->isIndexed()) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400139 for (uint16_t i = 0; i < numPts; ++i) {
140 appendCountourEdgeIndices(firstCPtIdx + i);
141 }
142 }
143 }
144
145 void addPath(const SkPath& path, SkScalar srcSpaceTol) {
146 SkScalar srcSpaceTolSqd = srcSpaceTol * srcSpaceTol;
147
148 SkPath::Iter iter(path, false);
149 SkPoint pts[4];
150
151 bool done = false;
152 while (!done) {
Stephen White2cee2832017-08-23 09:37:16 -0400153 SkPath::Verb verb = iter.next(pts, false);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400154 switch (verb) {
155 case SkPath::kMove_Verb:
156 this->moveTo(pts[0]);
157 break;
158 case SkPath::kLine_Verb:
159 this->addLine(pts[1]);
160 break;
161 case SkPath::kConic_Verb:
162 this->addConic(iter.conicWeight(), pts, srcSpaceTolSqd, srcSpaceTol);
163 break;
164 case SkPath::kQuad_Verb:
165 this->addQuad(pts, srcSpaceTolSqd, srcSpaceTol);
166 break;
167 case SkPath::kCubic_Verb:
168 this->addCubic(pts, srcSpaceTolSqd, srcSpaceTol);
169 break;
170 case SkPath::kClose_Verb:
171 break;
172 case SkPath::kDone_Verb:
173 done = true;
174 }
175 }
176 }
177
178 static bool PathHasMultipleSubpaths(const SkPath& path) {
179 bool first = true;
180
181 SkPath::Iter iter(path, false);
182 SkPath::Verb verb;
183
184 SkPoint pts[4];
Brian Salomon29f9ed42017-11-29 10:52:00 -0500185 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400186 if (SkPath::kMove_Verb == verb && !first) {
187 return true;
188 }
189 first = false;
190 }
191 return false;
192 }
193
194private:
195 /**
196 * Derived properties
197 * TODO: Cache some of these for better performance, rather than re-computing?
198 */
Brian Salomon763abf02018-05-01 18:49:38 +0000199 bool isIndexed() const {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000200 return GrPrimitiveType::kLines == fPrimitiveType ||
201 GrPrimitiveType::kTriangles == fPrimitiveType;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400202 }
203 bool isHairline() const {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000204 return GrPrimitiveType::kLines == fPrimitiveType ||
205 GrPrimitiveType::kLineStrip == fPrimitiveType;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400206 }
207 int indexScale() const {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000208 switch (fPrimitiveType) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400209 case GrPrimitiveType::kLines:
210 return 2;
211 case GrPrimitiveType::kTriangles:
212 return 3;
213 default:
214 return 0;
215 }
216 }
217
218 uint16_t currentIndex() const { return fCurVert - fVertices; }
219
Brian Osman49b7b6f2017-06-20 14:43:58 -0400220 // Allocate vertex and (possibly) index buffers
221 void allocNewBuffers() {
222 // Ensure that we always get enough verts for a worst-case quad/cubic, plus leftover points
223 // from previous mesh piece (up to two verts to continue fanning). If we can't get that
224 // many, ask for a much larger number. This needs to be fairly big to handle quads/cubics,
225 // which have a worst-case of 1k points.
226 static const int kMinVerticesPerChunk = GrPathUtils::kMaxPointsPerCurve + 2;
227 static const int kFallbackVerticesPerChunk = 16384;
228
229 fVertices = static_cast<SkPoint*>(fTarget->makeVertexSpaceAtLeast(fVertexStride,
230 kMinVerticesPerChunk,
231 kFallbackVerticesPerChunk,
232 &fVertexBuffer,
233 &fFirstVertex,
234 &fVerticesInChunk));
235
Brian Salomon763abf02018-05-01 18:49:38 +0000236 if (this->isIndexed()) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400237 // Similar to above: Ensure we get enough indices for one worst-case quad/cubic.
238 // No extra indices are needed for stitching, though. If we can't get that many, ask
239 // for enough to match our large vertex request.
240 const int kMinIndicesPerChunk = GrPathUtils::kMaxPointsPerCurve * this->indexScale();
241 const int kFallbackIndicesPerChunk = kFallbackVerticesPerChunk * this->indexScale();
242
243 fIndices = fTarget->makeIndexSpaceAtLeast(kMinIndicesPerChunk, kFallbackIndicesPerChunk,
244 &fIndexBuffer, &fFirstIndex,
245 &fIndicesInChunk);
246 }
Brian Osman3902d402017-06-20 15:36:31 -0400247
248 fCurVert = fVertices;
249 fCurIdx = fIndices;
250 fSubpathIndexStart = 0;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400251 }
252
253 void appendCountourEdgeIndices(uint16_t edgeV0Idx) {
254 // When drawing lines we're appending line segments along the countour. When applying the
255 // other fill rules we're drawing triangle fans around the start of the current (sub)path.
256 if (!this->isHairline()) {
257 *(fCurIdx++) = fSubpathIndexStart;
258 }
259 *(fCurIdx++) = edgeV0Idx;
260 *(fCurIdx++) = edgeV0Idx + 1;
261 }
262
263 // Emits a single draw with all accumulated vertex/index data
Brian Osman3902d402017-06-20 15:36:31 -0400264 void emitMeshAndPutBackReserve() {
265 int vertexCount = fCurVert - fVertices;
266 int indexCount = fCurIdx - fIndices;
267 SkASSERT(vertexCount <= fVerticesInChunk);
268 SkASSERT(indexCount <= fIndicesInChunk);
269
Brian Salomon763abf02018-05-01 18:49:38 +0000270 if (this->isIndexed() ? SkToBool(indexCount) : SkToBool(vertexCount)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000271 GrMesh* mesh = fTarget->allocMesh(fPrimitiveType);
Brian Salomon763abf02018-05-01 18:49:38 +0000272 if (!this->isIndexed()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000273 mesh->setNonIndexedNonInstanced(vertexCount);
Brian Salomon763abf02018-05-01 18:49:38 +0000274 } else {
Brian Salomon12d22642019-01-29 14:38:50 -0500275 mesh->setIndexed(std::move(fIndexBuffer), indexCount, fFirstIndex, 0,
276 vertexCount - 1, GrPrimitiveRestart::kNo);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400277 }
Brian Salomon12d22642019-01-29 14:38:50 -0500278 mesh->setVertexData(std::move(fVertexBuffer), fFirstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700279 fTarget->recordDraw(fGeometryProcessor, mesh);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400280 }
Brian Osman3902d402017-06-20 15:36:31 -0400281
282 fTarget->putBackIndices((size_t)(fIndicesInChunk - indexCount));
283 fTarget->putBackVertices((size_t)(fVerticesInChunk - vertexCount), fVertexStride);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400284 }
285
286 void needSpace(int vertsNeeded, int indicesNeeded = 0) {
287 if (fCurVert + vertsNeeded > fVertices + fVerticesInChunk ||
288 fCurIdx + indicesNeeded > fIndices + fIndicesInChunk) {
289 // We are about to run out of space (possibly)
290
291 // To maintain continuity, we need to remember one or two points from the current mesh.
292 // Lines only need the last point, fills need the first point from the current contour.
293 // We always grab both here, and append the ones we need at the end of this process.
294 SkPoint lastPt = *(fCurVert - 1);
Brian Osman3902d402017-06-20 15:36:31 -0400295 SkASSERT(fSubpathIndexStart < fVerticesInChunk);
296 SkPoint subpathStartPt = fVertices[fSubpathIndexStart];
Brian Osman49b7b6f2017-06-20 14:43:58 -0400297
Brian Osman3902d402017-06-20 15:36:31 -0400298 // Draw the mesh we've accumulated, and put back any unused space
299 this->emitMeshAndPutBackReserve();
Brian Osman49b7b6f2017-06-20 14:43:58 -0400300
Brian Osman3902d402017-06-20 15:36:31 -0400301 // Get new buffers
Brian Osman49b7b6f2017-06-20 14:43:58 -0400302 this->allocNewBuffers();
303
Brian Osman49b7b6f2017-06-20 14:43:58 -0400304 // Append copies of the points we saved so the two meshes will weld properly
305 if (!this->isHairline()) {
306 *(fCurVert++) = subpathStartPt;
307 }
308 *(fCurVert++) = lastPt;
309 }
310 }
311
Brian Salomon7eae3e02018-08-07 14:02:38 +0000312 GrPrimitiveType fPrimitiveType;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400313 GrMeshDrawOp::Target* fTarget;
314 size_t fVertexStride;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000315 sk_sp<const GrGeometryProcessor> fGeometryProcessor;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400316
Brian Salomon12d22642019-01-29 14:38:50 -0500317 sk_sp<const GrBuffer> fVertexBuffer;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400318 int fFirstVertex;
319 int fVerticesInChunk;
320 SkPoint* fVertices;
321 SkPoint* fCurVert;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400322
Brian Salomon12d22642019-01-29 14:38:50 -0500323 sk_sp<const GrBuffer> fIndexBuffer;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400324 int fFirstIndex;
325 int fIndicesInChunk;
326 uint16_t* fIndices;
327 uint16_t* fCurIdx;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400328 uint16_t fSubpathIndexStart;
329};
egdanielaf18a092015-01-05 10:22:28 -0800330
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400331class DefaultPathOp final : public GrMeshDrawOp {
332private:
333 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
334
joshualitt332c7292015-02-23 08:44:31 -0800335public:
Brian Salomon25a88092016-12-01 09:36:50 -0500336 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700337
Robert Phillipsb97da532019-02-12 15:24:12 -0500338 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400339 GrPaint&& paint,
340 const SkPath& path,
341 SkScalar tolerance,
342 uint8_t coverage,
343 const SkMatrix& viewMatrix,
344 bool isHairline,
345 GrAAType aaType,
346 const SkRect& devBounds,
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400347 const GrUserStencilSettings* stencilSettings) {
Robert Phillips7c525e62018-06-12 10:11:12 -0400348 return Helper::FactoryHelper<DefaultPathOp>(context, std::move(paint), path, tolerance,
349 coverage, viewMatrix, isHairline, aaType,
350 devBounds, stencilSettings);
bsalomon@google.com30085192011-08-19 15:42:31 +0000351 }
352
Brian Salomon780dad12016-12-15 18:08:40 -0500353 const char* name() const override { return "DefaultPathOp"; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000354
Brian Salomon7d94bb52018-10-12 14:37:19 -0400355 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400356 fHelper.visitProxies(func);
357 }
358
Brian Osman9a390ac2018-11-12 09:47:48 -0500359#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -0500360 SkString dumpInfo() const override {
361 SkString string;
Brian Osmancf860852018-10-31 14:04:39 -0400362 string.appendf("Color: 0x%08x Count: %d\n", fColor.toBytes_RGBA(), fPaths.count());
Brian Salomon780dad12016-12-15 18:08:40 -0500363 for (const auto& path : fPaths) {
364 string.appendf("Tolerance: %.2f\n", path.fTolerance);
Brian Salomon7c3e7182016-12-01 09:35:30 -0500365 }
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400366 string += fHelper.dumpInfo();
367 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -0500368 return string;
369 }
Brian Osman9a390ac2018-11-12 09:47:48 -0500370#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -0500371
Brian Osmancf860852018-10-31 14:04:39 -0400372 DefaultPathOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color, const SkPath& path,
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400373 SkScalar tolerance, uint8_t coverage, const SkMatrix& viewMatrix, bool isHairline,
374 GrAAType aaType, const SkRect& devBounds,
375 const GrUserStencilSettings* stencilSettings)
Brian Salomon780dad12016-12-15 18:08:40 -0500376 : INHERITED(ClassID())
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400377 , fHelper(helperArgs, aaType, stencilSettings)
Brian Salomon780dad12016-12-15 18:08:40 -0500378 , fColor(color)
379 , fCoverage(coverage)
380 , fViewMatrix(viewMatrix)
381 , fIsHairline(isHairline) {
382 fPaths.emplace_back(PathData{path, tolerance});
joshualitt332c7292015-02-23 08:44:31 -0800383
Brian Salomon780dad12016-12-15 18:08:40 -0500384 this->setBounds(devBounds, HasAABloat::kNo,
385 isHairline ? IsZeroArea::kYes : IsZeroArea::kNo);
386 }
387
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400388 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
389
Chris Dalton4b62aed2019-01-15 11:53:00 -0700390 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400391 GrProcessorAnalysisCoverage gpCoverage =
392 this->coverage() == 0xFF ? GrProcessorAnalysisCoverage::kNone
393 : GrProcessorAnalysisCoverage::kSingleChannel;
Chris Dalton4b62aed2019-01-15 11:53:00 -0700394 return fHelper.finalizeProcessors(caps, clip, gpCoverage, &fColor);
Brian Salomon92aee3d2016-12-21 09:20:25 -0500395 }
396
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400397private:
Brian Salomon91326c32017-08-09 16:02:19 -0400398 void onPrepareDraws(Target* target) override {
bungeman06ca8ec2016-06-09 08:01:03 -0700399 sk_sp<GrGeometryProcessor> gp;
joshualittdf0c5572015-08-03 11:35:28 -0700400 {
401 using namespace GrDefaultGeoProcFactory;
402 Color color(this->color());
403 Coverage coverage(this->coverage());
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400404 LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
405 : LocalCoords::kUnused_Type);
Ruiqi Maob609e6d2018-07-17 10:19:38 -0400406 gp = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
407 color,
408 coverage,
409 localCoords,
410 this->viewMatrix());
joshualittdf0c5572015-08-03 11:35:28 -0700411 }
joshualitt332c7292015-02-23 08:44:31 -0800412
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500413 SkASSERT(gp->vertexStride() == sizeof(SkPoint));
joshualitt332c7292015-02-23 08:44:31 -0800414
Brian Salomon763abf02018-05-01 18:49:38 +0000415 int instanceCount = fPaths.count();
416
417 // We avoid indices when we have a single hairline contour.
418 bool isIndexed = !this->isHairline() || instanceCount > 1 ||
419 PathGeoBuilder::PathHasMultipleSubpaths(fPaths[0].fPath);
Brian Osman7f95dfc2017-06-09 14:43:49 +0000420
421 // determine primitiveType
Brian Osman7f95dfc2017-06-09 14:43:49 +0000422 GrPrimitiveType primitiveType;
423 if (this->isHairline()) {
Brian Salomon763abf02018-05-01 18:49:38 +0000424 primitiveType = isIndexed ? GrPrimitiveType::kLines : GrPrimitiveType::kLineStrip;
Brian Osman7f95dfc2017-06-09 14:43:49 +0000425 } else {
Brian Salomon06c33042018-04-25 15:45:27 -0400426 primitiveType = GrPrimitiveType::kTriangles;
Brian Osman7f95dfc2017-06-09 14:43:49 +0000427 }
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700428 PathGeoBuilder pathGeoBuilder(primitiveType, target, std::move(gp));
Brian Osman7f95dfc2017-06-09 14:43:49 +0000429
430 // fill buffers
Brian Salomon763abf02018-05-01 18:49:38 +0000431 for (int i = 0; i < instanceCount; i++) {
432 const PathData& args = fPaths[i];
433 pathGeoBuilder.addPath(args.fPath, args.fTolerance);
Brian Osman7f95dfc2017-06-09 14:43:49 +0000434 }
joshualitt332c7292015-02-23 08:44:31 -0800435 }
436
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700437 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
438 fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
439 }
440
Brian Salomon7eae3e02018-08-07 14:02:38 +0000441 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon780dad12016-12-15 18:08:40 -0500442 DefaultPathOp* that = t->cast<DefaultPathOp>();
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400443 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000444 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -0700445 }
446
joshualitt332c7292015-02-23 08:44:31 -0800447 if (this->color() != that->color()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000448 return CombineResult::kCannotCombine;
joshualitt332c7292015-02-23 08:44:31 -0800449 }
450
451 if (this->coverage() != that->coverage()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000452 return CombineResult::kCannotCombine;
joshualitt332c7292015-02-23 08:44:31 -0800453 }
454
455 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000456 return CombineResult::kCannotCombine;
joshualitt332c7292015-02-23 08:44:31 -0800457 }
458
459 if (this->isHairline() != that->isHairline()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000460 return CombineResult::kCannotCombine;
joshualitt332c7292015-02-23 08:44:31 -0800461 }
462
Brian Salomon780dad12016-12-15 18:08:40 -0500463 fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
Brian Salomon7eae3e02018-08-07 14:02:38 +0000464 return CombineResult::kMerged;
joshualitt332c7292015-02-23 08:44:31 -0800465 }
466
Brian Osmancf860852018-10-31 14:04:39 -0400467 const SkPMColor4f& color() const { return fColor; }
Brian Salomon780dad12016-12-15 18:08:40 -0500468 uint8_t coverage() const { return fCoverage; }
Brian Salomon780dad12016-12-15 18:08:40 -0500469 const SkMatrix& viewMatrix() const { return fViewMatrix; }
470 bool isHairline() const { return fIsHairline; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000471
Brian Salomon780dad12016-12-15 18:08:40 -0500472 struct PathData {
bsalomon0432dd62016-06-30 07:19:27 -0700473 SkPath fPath;
474 SkScalar fTolerance;
475 };
476
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400477 SkSTArray<1, PathData, true> fPaths;
478 Helper fHelper;
Brian Osmancf860852018-10-31 14:04:39 -0400479 SkPMColor4f fColor;
Brian Salomon780dad12016-12-15 18:08:40 -0500480 uint8_t fCoverage;
481 SkMatrix fViewMatrix;
Brian Salomon780dad12016-12-15 18:08:40 -0500482 bool fIsHairline;
reed1b55a962015-09-17 20:16:13 -0700483
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400484 typedef GrMeshDrawOp INHERITED;
joshualitt332c7292015-02-23 08:44:31 -0800485};
bsalomon@google.com30085192011-08-19 15:42:31 +0000486
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400487} // anonymous namespace
488
Brian Osman11052242016-10-27 14:47:55 -0400489bool GrDefaultPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetContext,
Brian Salomon82f44312017-01-11 13:42:54 -0500490 GrPaint&& paint,
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500491 GrAAType aaType,
robertphillipsd2b6d642016-07-21 08:55:08 -0700492 const GrUserStencilSettings& userStencilSettings,
cdalton862cff32016-05-12 15:09:48 -0700493 const GrClip& clip,
joshualitt8059eb92014-12-29 15:10:07 -0800494 const SkMatrix& viewMatrix,
bsalomon8acedde2016-06-24 10:42:16 -0700495 const GrShape& shape,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000496 bool stencilOnly) {
Robert Phillipsb97da532019-02-12 15:24:12 -0500497 auto context = renderTargetContext->surfPriv().getContext();
Robert Phillips7c525e62018-06-12 10:11:12 -0400498
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500499 SkASSERT(GrAAType::kCoverage != aaType);
bsalomon8acedde2016-06-24 10:42:16 -0700500 SkPath path;
501 shape.asPath(&path);
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000502
503 SkScalar hairlineCoverage;
joshualitt2e3b3e32014-12-09 13:31:14 -0800504 uint8_t newCoverage = 0xff;
bsalomon6663acf2016-05-10 09:14:17 -0700505 bool isHairline = false;
bsalomon8acedde2016-06-24 10:42:16 -0700506 if (IsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) {
joshualitt2e3b3e32014-12-09 13:31:14 -0800507 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
bsalomon6663acf2016-05-10 09:14:17 -0700508 isHairline = true;
509 } else {
bsalomon8acedde2016-06-24 10:42:16 -0700510 SkASSERT(shape.style().isSimpleFill());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000511 }
512
cdalton93a379b2016-05-11 13:58:08 -0700513 int passCount = 0;
Brian Salomonf0861672017-05-08 11:10:10 -0400514 const GrUserStencilSettings* passes[2];
cdalton93a379b2016-05-11 13:58:08 -0700515 bool reverse = false;
516 bool lastPassIsBounds;
bsalomon@google.com30085192011-08-19 15:42:31 +0000517
joshualitt332c7292015-02-23 08:44:31 -0800518 if (isHairline) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000519 passCount = 1;
520 if (stencilOnly) {
521 passes[0] = &gDirectToStencil;
522 } else {
robertphillipsd2b6d642016-07-21 08:55:08 -0700523 passes[0] = &userStencilSettings;
bsalomon@google.com30085192011-08-19 15:42:31 +0000524 }
525 lastPassIsBounds = false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000526 } else {
bsalomon8acedde2016-06-24 10:42:16 -0700527 if (single_pass_shape(shape)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000528 passCount = 1;
529 if (stencilOnly) {
530 passes[0] = &gDirectToStencil;
531 } else {
robertphillipsd2b6d642016-07-21 08:55:08 -0700532 passes[0] = &userStencilSettings;
bsalomon@google.com30085192011-08-19 15:42:31 +0000533 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000534 lastPassIsBounds = false;
535 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000536 switch (path.getFillType()) {
sugoi@google.com12b4e272012-12-06 20:13:11 +0000537 case SkPath::kInverseEvenOdd_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000538 reverse = true;
539 // fallthrough
sugoi@google.com12b4e272012-12-06 20:13:11 +0000540 case SkPath::kEvenOdd_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000541 passes[0] = &gEOStencilPass;
542 if (stencilOnly) {
543 passCount = 1;
544 lastPassIsBounds = false;
545 } else {
546 passCount = 2;
547 lastPassIsBounds = true;
548 if (reverse) {
549 passes[1] = &gInvEOColorPass;
550 } else {
551 passes[1] = &gEOColorPass;
552 }
553 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000554 break;
555
sugoi@google.com12b4e272012-12-06 20:13:11 +0000556 case SkPath::kInverseWinding_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000557 reverse = true;
558 // fallthrough
sugoi@google.com12b4e272012-12-06 20:13:11 +0000559 case SkPath::kWinding_FillType:
Brian Salomon15b25092017-05-08 11:10:53 -0400560 passes[0] = &gWindStencilPass;
Brian Salomonf0861672017-05-08 11:10:10 -0400561 passCount = 2;
bsalomon@google.com30085192011-08-19 15:42:31 +0000562 if (stencilOnly) {
563 lastPassIsBounds = false;
564 --passCount;
565 } else {
566 lastPassIsBounds = true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000567 if (reverse) {
568 passes[passCount-1] = &gInvWindColorPass;
569 } else {
570 passes[passCount-1] = &gWindColorPass;
571 }
572 }
573 break;
574 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000575 SkDEBUGFAIL("Unknown path fFill!");
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000576 return false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000577 }
578 }
579 }
580
senorblanco2b4bb072015-04-22 13:45:18 -0700581 SkScalar tol = GrPathUtils::kDefaultTolerance;
joshualitt332c7292015-02-23 08:44:31 -0800582 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
583
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000584 SkRect devBounds;
Robert Phillipsec325342017-10-30 18:02:48 +0000585 GetPathDevBounds(path,
586 renderTargetContext->asRenderTargetProxy()->worstCaseWidth(),
587 renderTargetContext->asRenderTargetProxy()->worstCaseHeight(),
588 viewMatrix, &devBounds);
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000589
bsalomon@google.com30085192011-08-19 15:42:31 +0000590 for (int p = 0; p < passCount; ++p) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000591 if (lastPassIsBounds && (p == passCount-1)) {
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000592 SkRect bounds;
joshualittd27f73e2014-12-29 07:43:36 -0800593 SkMatrix localMatrix = SkMatrix::I();
bsalomon@google.com30085192011-08-19 15:42:31 +0000594 if (reverse) {
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000595 // draw over the dev bounds (which will be the whole dst surface for inv fill).
596 bounds = devBounds;
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000597 SkMatrix vmi;
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000598 // mapRect through persp matrix may not be correct
joshualitt8059eb92014-12-29 15:10:07 -0800599 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000600 vmi.mapRect(&bounds);
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000601 } else {
joshualittd27f73e2014-12-29 07:43:36 -0800602 if (!viewMatrix.invert(&localMatrix)) {
603 return false;
604 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000605 }
606 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000607 bounds = path.getBounds();
bsalomon@google.com30085192011-08-19 15:42:31 +0000608 }
joshualitt8059eb92014-12-29 15:10:07 -0800609 const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
610 viewMatrix;
Michael Ludwig72ab3462018-12-10 12:43:36 -0500611 // This is a non-coverage aa rect op since we assert aaType != kCoverage at the start
Mike Klein16885072018-12-11 09:54:31 -0500612 assert_alive(paint);
Brian Salomonac70f842017-05-08 10:43:33 -0400613 renderTargetContext->addDrawOp(
614 clip,
Michael Ludwig72ab3462018-12-10 12:43:36 -0500615 GrFillRectOp::MakeWithLocalMatrix(context, std::move(paint), aaType, viewM,
616 localMatrix, bounds, passes[p]));
robertphillips976f5f02016-06-03 10:59:20 -0700617 } else {
Brian Salomond4652ca2017-01-13 12:11:36 -0500618 bool stencilPass = stencilOnly || passCount > 1;
Brian Salomonb74ef032017-08-10 12:46:01 -0400619 std::unique_ptr<GrDrawOp> op;
Brian Salomond4652ca2017-01-13 12:11:36 -0500620 if (stencilPass) {
Brian Salomonb74ef032017-08-10 12:46:01 -0400621 GrPaint stencilPaint;
622 stencilPaint.setXPFactory(GrDisableColorXPFactory::Get());
Robert Phillips7c525e62018-06-12 10:11:12 -0400623 op = DefaultPathOp::Make(context, std::move(stencilPaint), path, srcSpaceTol,
624 newCoverage, viewMatrix, isHairline, aaType, devBounds,
625 passes[p]);
Brian Salomonb74ef032017-08-10 12:46:01 -0400626 } else {
Mike Klein16885072018-12-11 09:54:31 -0500627 assert_alive(paint);
Robert Phillips7c525e62018-06-12 10:11:12 -0400628 op = DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, newCoverage,
Brian Salomonb74ef032017-08-10 12:46:01 -0400629 viewMatrix, isHairline, aaType, devBounds, passes[p]);
Brian Salomond4652ca2017-01-13 12:11:36 -0500630 }
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400631 renderTargetContext->addDrawOp(clip, std::move(op));
bsalomon@google.com30085192011-08-19 15:42:31 +0000632 }
633 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000634 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000635}
636
Chris Dalton5ed44232017-09-07 13:22:46 -0600637GrPathRenderer::CanDrawPath
638GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
Eric Karl5c779752017-05-08 12:02:07 -0700639 bool isHairline = IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr);
640 // If we aren't a single_pass_shape or hairline, we require stencil buffers.
Greg Danielbe7fc462019-01-03 16:40:42 -0500641 if (!(single_pass_shape(*args.fShape) || isHairline) &&
642 (args.fCaps->avoidStencilBuffers() || args.fTargetIsWrappedVkSecondaryCB)) {
Chris Dalton5ed44232017-09-07 13:22:46 -0600643 return CanDrawPath::kNo;
Eric Karl5c779752017-05-08 12:02:07 -0700644 }
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500645 // This can draw any path with any simple fill style but doesn't do coverage-based antialiasing.
Chris Dalton5ed44232017-09-07 13:22:46 -0600646 if (GrAAType::kCoverage == args.fAAType ||
647 (!args.fShape->style().isSimpleFill() && !isHairline)) {
648 return CanDrawPath::kNo;
649 }
650 // This is the fallback renderer for when a path is too complicated for the others to draw.
651 return CanDrawPath::kAsBackup;
bsalomon@google.com30085192011-08-19 15:42:31 +0000652}
653
bsalomon0aff2fa2015-07-31 06:48:27 -0700654bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400655 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700656 "GrDefaultPathRenderer::onDrawPath");
Brian Osman11052242016-10-27 14:47:55 -0400657 return this->internalDrawPath(args.fRenderTargetContext,
Brian Salomon82f44312017-01-11 13:42:54 -0500658 std::move(args.fPaint),
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500659 args.fAAType,
robertphillipsd2b6d642016-07-21 08:55:08 -0700660 *args.fUserStencilSettings,
cdalton862cff32016-05-12 15:09:48 -0700661 *args.fClip,
bsalomon0aff2fa2015-07-31 06:48:27 -0700662 *args.fViewMatrix,
bsalomon8acedde2016-06-24 10:42:16 -0700663 *args.fShape,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000664 false);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000665}
666
bsalomon0aff2fa2015-07-31 06:48:27 -0700667void GrDefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400668 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700669 "GrDefaultPathRenderer::onStencilPath");
bsalomon8acedde2016-06-24 10:42:16 -0700670 SkASSERT(!args.fShape->inverseFilled());
robertphillips976f5f02016-06-03 10:59:20 -0700671
672 GrPaint paint;
Brian Salomona1633922017-01-09 11:46:10 -0500673 paint.setXPFactory(GrDisableColorXPFactory::Get());
robertphillips976f5f02016-06-03 10:59:20 -0700674
Brian Salomon82f44312017-01-11 13:42:54 -0500675 this->internalDrawPath(args.fRenderTargetContext, std::move(paint), args.fAAType,
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500676 GrUserStencilSettings::kUnused, *args.fClip, *args.fViewMatrix,
677 *args.fShape, true);
bsalomon@google.com30085192011-08-19 15:42:31 +0000678}
joshualitt622d3ad2015-05-07 08:13:11 -0700679
680///////////////////////////////////////////////////////////////////////////////////////////////////
681
Hal Canary6f6961e2017-01-31 13:50:44 -0500682#if GR_TEST_UTILS
joshualitt622d3ad2015-05-07 08:13:11 -0700683
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400684GR_DRAW_OP_TEST_DEFINE(DefaultPathOp) {
joshualitt622d3ad2015-05-07 08:13:11 -0700685 SkMatrix viewMatrix = GrTest::TestMatrix(random);
686
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500687 // For now just hairlines because the other types of draws require two ops.
688 // TODO we should figure out a way to combine the stencil and cover steps into one op.
bsalomon6663acf2016-05-10 09:14:17 -0700689 GrStyle style(SkStrokeRec::kHairline_InitStyle);
joshualitt622d3ad2015-05-07 08:13:11 -0700690 SkPath path = GrTest::TestPath(random);
691
692 // Compute srcSpaceTol
693 SkRect bounds = path.getBounds();
694 SkScalar tol = GrPathUtils::kDefaultTolerance;
695 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
696
joshualitt622d3ad2015-05-07 08:13:11 -0700697 viewMatrix.mapRect(&bounds);
698 uint8_t coverage = GrRandomCoverage(random);
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400699 GrAAType aaType = GrAAType::kNone;
700 if (GrFSAAType::kUnifiedMSAA == fsaaType && random->nextBool()) {
701 aaType = GrAAType::kMSAA;
702 }
Robert Phillips7c525e62018-06-12 10:11:12 -0400703 return DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, coverage, viewMatrix,
704 true, aaType, bounds, GrGetRandomStencil(random, context));
joshualitt622d3ad2015-05-07 08:13:11 -0700705}
706
707#endif