blob: 2e3af82c794202675dab73c57c2ac7c82a874801 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/gpu/ops/GrDefaultPathRenderer.h"
Robert Phillipsbe9aff22019-02-15 11:33:22 -05009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkString.h"
11#include "include/core/SkStrokeRec.h"
12#include "src/core/SkGeometry.h"
13#include "src/core/SkTLazy.h"
14#include "src/core/SkTraceEvent.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040015#include "src/gpu/GrAuditTrail.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050016#include "src/gpu/GrCaps.h"
17#include "src/gpu/GrDefaultGeoProcFactory.h"
18#include "src/gpu/GrDrawOpTest.h"
19#include "src/gpu/GrFixedClip.h"
20#include "src/gpu/GrMesh.h"
21#include "src/gpu/GrOpFlushState.h"
Michael Ludwigaa1b6b32019-05-29 14:43:13 -040022#include "src/gpu/GrRenderTargetContextPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050023#include "src/gpu/GrStyle.h"
24#include "src/gpu/GrSurfaceContextPriv.h"
Michael Ludwig663afe52019-06-03 16:46:19 -040025#include "src/gpu/geometry/GrPathUtils.h"
26#include "src/gpu/geometry/GrShape.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050027#include "src/gpu/ops/GrMeshDrawOp.h"
28#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
joshualitt74417822015-08-07 11:42:16 -070029
Brian Salomon15b25092017-05-08 11:10:53 -040030GrDefaultPathRenderer::GrDefaultPathRenderer() {
bsalomon@google.com289533a2011-10-27 12:34:25 +000031}
32
bsalomon@google.com30085192011-08-19 15:42:31 +000033////////////////////////////////////////////////////////////////////////////////
34// Helpers for drawPath
35
bsalomon@google.com30085192011-08-19 15:42:31 +000036#define STENCIL_OFF 0 // Always disable stencil (even when needed)
37
bsalomon8acedde2016-06-24 10:42:16 -070038static inline bool single_pass_shape(const GrShape& shape) {
bsalomon@google.com30085192011-08-19 15:42:31 +000039#if STENCIL_OFF
40 return true;
41#else
bsalomonee432412016-06-27 07:18:18 -070042 // Inverse fill is always two pass.
43 if (shape.inverseFilled()) {
44 return false;
45 }
46 // This path renderer only accepts simple fill paths or stroke paths that are either hairline
47 // or have a stroke width small enough to treat as hairline. Hairline paths are always single
48 // pass. Filled paths are single pass if they're convex.
49 if (shape.style().isSimpleFill()) {
bsalomon8acedde2016-06-24 10:42:16 -070050 return shape.knownToBeConvex();
bsalomon@google.com30085192011-08-19 15:42:31 +000051 }
bsalomonee432412016-06-27 07:18:18 -070052 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +000053#endif
54}
55
joshualitt9853cce2014-11-17 14:22:48 -080056GrPathRenderer::StencilSupport
bsalomon8acedde2016-06-24 10:42:16 -070057GrDefaultPathRenderer::onGetStencilSupport(const GrShape& shape) const {
58 if (single_pass_shape(shape)) {
bsalomon@google.com45a15f52012-12-10 19:10:17 +000059 return GrPathRenderer::kNoRestriction_StencilSupport;
60 } else {
61 return GrPathRenderer::kStencilOnly_StencilSupport;
62 }
bsalomon@google.com30085192011-08-19 15:42:31 +000063}
64
Brian Salomonee3e0ba2017-07-13 16:40:46 -040065namespace {
66
Brian Osman49b7b6f2017-06-20 14:43:58 -040067class PathGeoBuilder {
68public:
Brian Salomon763abf02018-05-01 18:49:38 +000069 PathGeoBuilder(GrPrimitiveType primitiveType, GrMeshDrawOp::Target* target,
Chris Dalton07cdcfc92019-02-26 11:13:22 -070070 sk_sp<const GrGeometryProcessor> geometryProcessor)
Brian Salomon7eae3e02018-08-07 14:02:38 +000071 : fPrimitiveType(primitiveType)
Brian Osman49b7b6f2017-06-20 14:43:58 -040072 , fTarget(target)
73 , fVertexStride(sizeof(SkPoint))
Brian Salomon7eae3e02018-08-07 14:02:38 +000074 , fGeometryProcessor(std::move(geometryProcessor))
Brian Osman49b7b6f2017-06-20 14:43:58 -040075 , fFirstIndex(0)
76 , fIndicesInChunk(0)
77 , fIndices(nullptr) {
78 this->allocNewBuffers();
Brian Osmaneb86b702017-06-07 11:38:31 -040079 }
80
Brian Osman49b7b6f2017-06-20 14:43:58 -040081 ~PathGeoBuilder() {
Brian Osman3902d402017-06-20 15:36:31 -040082 this->emitMeshAndPutBackReserve();
Brian Osman49b7b6f2017-06-20 14:43:58 -040083 }
84
85 /**
86 * Path verbs
87 */
88 void moveTo(const SkPoint& p) {
89 needSpace(1);
90
91 fSubpathIndexStart = this->currentIndex();
92 *(fCurVert++) = p;
93 }
94
95 void addLine(const SkPoint& p) {
96 needSpace(1, this->indexScale());
97
Brian Salomon763abf02018-05-01 18:49:38 +000098 if (this->isIndexed()) {
Brian Osman49b7b6f2017-06-20 14:43:58 -040099 uint16_t prevIdx = this->currentIndex() - 1;
100 appendCountourEdgeIndices(prevIdx);
101 }
102 *(fCurVert++) = p;
103 }
104
105 void addQuad(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
106 this->needSpace(GrPathUtils::kMaxPointsPerCurve,
107 GrPathUtils::kMaxPointsPerCurve * this->indexScale());
108
109 // First pt of quad is the pt we ended on in previous step
110 uint16_t firstQPtIdx = this->currentIndex() - 1;
111 uint16_t numPts = (uint16_t)GrPathUtils::generateQuadraticPoints(
112 pts[0], pts[1], pts[2], srcSpaceTolSqd, &fCurVert,
113 GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
Brian Salomon763abf02018-05-01 18:49:38 +0000114 if (this->isIndexed()) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400115 for (uint16_t i = 0; i < numPts; ++i) {
116 appendCountourEdgeIndices(firstQPtIdx + i);
117 }
egdanielaf18a092015-01-05 10:22:28 -0800118 }
119 }
Brian Osman49b7b6f2017-06-20 14:43:58 -0400120
121 void addConic(SkScalar weight, const SkPoint pts[], SkScalar srcSpaceTolSqd,
122 SkScalar srcSpaceTol) {
123 SkAutoConicToQuads converter;
124 const SkPoint* quadPts = converter.computeQuads(pts, weight, srcSpaceTol);
125 for (int i = 0; i < converter.countQuads(); ++i) {
126 this->addQuad(quadPts + i * 2, srcSpaceTolSqd, srcSpaceTol);
127 }
128 }
129
130 void addCubic(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
131 this->needSpace(GrPathUtils::kMaxPointsPerCurve,
132 GrPathUtils::kMaxPointsPerCurve * this->indexScale());
133
134 // First pt of cubic is the pt we ended on in previous step
135 uint16_t firstCPtIdx = this->currentIndex() - 1;
136 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
137 pts[0], pts[1], pts[2], pts[3], srcSpaceTolSqd, &fCurVert,
138 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
Brian Salomon763abf02018-05-01 18:49:38 +0000139 if (this->isIndexed()) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400140 for (uint16_t i = 0; i < numPts; ++i) {
141 appendCountourEdgeIndices(firstCPtIdx + i);
142 }
143 }
144 }
145
146 void addPath(const SkPath& path, SkScalar srcSpaceTol) {
147 SkScalar srcSpaceTolSqd = srcSpaceTol * srcSpaceTol;
148
149 SkPath::Iter iter(path, false);
150 SkPoint pts[4];
151
152 bool done = false;
153 while (!done) {
Stephen White2cee2832017-08-23 09:37:16 -0400154 SkPath::Verb verb = iter.next(pts, false);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400155 switch (verb) {
156 case SkPath::kMove_Verb:
157 this->moveTo(pts[0]);
158 break;
159 case SkPath::kLine_Verb:
160 this->addLine(pts[1]);
161 break;
162 case SkPath::kConic_Verb:
163 this->addConic(iter.conicWeight(), pts, srcSpaceTolSqd, srcSpaceTol);
164 break;
165 case SkPath::kQuad_Verb:
166 this->addQuad(pts, srcSpaceTolSqd, srcSpaceTol);
167 break;
168 case SkPath::kCubic_Verb:
169 this->addCubic(pts, srcSpaceTolSqd, srcSpaceTol);
170 break;
171 case SkPath::kClose_Verb:
172 break;
173 case SkPath::kDone_Verb:
174 done = true;
175 }
176 }
177 }
178
179 static bool PathHasMultipleSubpaths(const SkPath& path) {
180 bool first = true;
181
182 SkPath::Iter iter(path, false);
183 SkPath::Verb verb;
184
185 SkPoint pts[4];
Brian Salomon29f9ed42017-11-29 10:52:00 -0500186 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400187 if (SkPath::kMove_Verb == verb && !first) {
188 return true;
189 }
190 first = false;
191 }
192 return false;
193 }
194
195private:
196 /**
197 * Derived properties
198 * TODO: Cache some of these for better performance, rather than re-computing?
199 */
Brian Salomon763abf02018-05-01 18:49:38 +0000200 bool isIndexed() const {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000201 return GrPrimitiveType::kLines == fPrimitiveType ||
202 GrPrimitiveType::kTriangles == fPrimitiveType;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400203 }
204 bool isHairline() const {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000205 return GrPrimitiveType::kLines == fPrimitiveType ||
206 GrPrimitiveType::kLineStrip == fPrimitiveType;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400207 }
208 int indexScale() const {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000209 switch (fPrimitiveType) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400210 case GrPrimitiveType::kLines:
211 return 2;
212 case GrPrimitiveType::kTriangles:
213 return 3;
214 default:
215 return 0;
216 }
217 }
218
219 uint16_t currentIndex() const { return fCurVert - fVertices; }
220
Brian Osman49b7b6f2017-06-20 14:43:58 -0400221 // Allocate vertex and (possibly) index buffers
222 void allocNewBuffers() {
223 // Ensure that we always get enough verts for a worst-case quad/cubic, plus leftover points
224 // from previous mesh piece (up to two verts to continue fanning). If we can't get that
225 // many, ask for a much larger number. This needs to be fairly big to handle quads/cubics,
226 // which have a worst-case of 1k points.
227 static const int kMinVerticesPerChunk = GrPathUtils::kMaxPointsPerCurve + 2;
228 static const int kFallbackVerticesPerChunk = 16384;
229
230 fVertices = static_cast<SkPoint*>(fTarget->makeVertexSpaceAtLeast(fVertexStride,
231 kMinVerticesPerChunk,
232 kFallbackVerticesPerChunk,
233 &fVertexBuffer,
234 &fFirstVertex,
235 &fVerticesInChunk));
236
Brian Salomon763abf02018-05-01 18:49:38 +0000237 if (this->isIndexed()) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400238 // Similar to above: Ensure we get enough indices for one worst-case quad/cubic.
239 // No extra indices are needed for stitching, though. If we can't get that many, ask
240 // for enough to match our large vertex request.
241 const int kMinIndicesPerChunk = GrPathUtils::kMaxPointsPerCurve * this->indexScale();
242 const int kFallbackIndicesPerChunk = kFallbackVerticesPerChunk * this->indexScale();
243
244 fIndices = fTarget->makeIndexSpaceAtLeast(kMinIndicesPerChunk, kFallbackIndicesPerChunk,
245 &fIndexBuffer, &fFirstIndex,
246 &fIndicesInChunk);
247 }
Brian Osman3902d402017-06-20 15:36:31 -0400248
249 fCurVert = fVertices;
250 fCurIdx = fIndices;
251 fSubpathIndexStart = 0;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400252 }
253
254 void appendCountourEdgeIndices(uint16_t edgeV0Idx) {
255 // When drawing lines we're appending line segments along the countour. When applying the
256 // other fill rules we're drawing triangle fans around the start of the current (sub)path.
257 if (!this->isHairline()) {
258 *(fCurIdx++) = fSubpathIndexStart;
259 }
260 *(fCurIdx++) = edgeV0Idx;
261 *(fCurIdx++) = edgeV0Idx + 1;
262 }
263
264 // Emits a single draw with all accumulated vertex/index data
Brian Osman3902d402017-06-20 15:36:31 -0400265 void emitMeshAndPutBackReserve() {
266 int vertexCount = fCurVert - fVertices;
267 int indexCount = fCurIdx - fIndices;
268 SkASSERT(vertexCount <= fVerticesInChunk);
269 SkASSERT(indexCount <= fIndicesInChunk);
270
Brian Salomon763abf02018-05-01 18:49:38 +0000271 if (this->isIndexed() ? SkToBool(indexCount) : SkToBool(vertexCount)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000272 GrMesh* mesh = fTarget->allocMesh(fPrimitiveType);
Brian Salomon763abf02018-05-01 18:49:38 +0000273 if (!this->isIndexed()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000274 mesh->setNonIndexedNonInstanced(vertexCount);
Brian Salomon763abf02018-05-01 18:49:38 +0000275 } else {
Brian Salomon12d22642019-01-29 14:38:50 -0500276 mesh->setIndexed(std::move(fIndexBuffer), indexCount, fFirstIndex, 0,
277 vertexCount - 1, GrPrimitiveRestart::kNo);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400278 }
Brian Salomon12d22642019-01-29 14:38:50 -0500279 mesh->setVertexData(std::move(fVertexBuffer), fFirstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700280 fTarget->recordDraw(fGeometryProcessor, mesh);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400281 }
Brian Osman3902d402017-06-20 15:36:31 -0400282
283 fTarget->putBackIndices((size_t)(fIndicesInChunk - indexCount));
284 fTarget->putBackVertices((size_t)(fVerticesInChunk - vertexCount), fVertexStride);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400285 }
286
287 void needSpace(int vertsNeeded, int indicesNeeded = 0) {
288 if (fCurVert + vertsNeeded > fVertices + fVerticesInChunk ||
289 fCurIdx + indicesNeeded > fIndices + fIndicesInChunk) {
290 // We are about to run out of space (possibly)
291
292 // To maintain continuity, we need to remember one or two points from the current mesh.
293 // Lines only need the last point, fills need the first point from the current contour.
294 // We always grab both here, and append the ones we need at the end of this process.
295 SkPoint lastPt = *(fCurVert - 1);
Brian Osman3902d402017-06-20 15:36:31 -0400296 SkASSERT(fSubpathIndexStart < fVerticesInChunk);
297 SkPoint subpathStartPt = fVertices[fSubpathIndexStart];
Brian Osman49b7b6f2017-06-20 14:43:58 -0400298
Brian Osman3902d402017-06-20 15:36:31 -0400299 // Draw the mesh we've accumulated, and put back any unused space
300 this->emitMeshAndPutBackReserve();
Brian Osman49b7b6f2017-06-20 14:43:58 -0400301
Brian Osman3902d402017-06-20 15:36:31 -0400302 // Get new buffers
Brian Osman49b7b6f2017-06-20 14:43:58 -0400303 this->allocNewBuffers();
304
Brian Osman49b7b6f2017-06-20 14:43:58 -0400305 // Append copies of the points we saved so the two meshes will weld properly
306 if (!this->isHairline()) {
307 *(fCurVert++) = subpathStartPt;
308 }
309 *(fCurVert++) = lastPt;
310 }
311 }
312
Brian Salomon7eae3e02018-08-07 14:02:38 +0000313 GrPrimitiveType fPrimitiveType;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400314 GrMeshDrawOp::Target* fTarget;
315 size_t fVertexStride;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000316 sk_sp<const GrGeometryProcessor> fGeometryProcessor;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400317
Brian Salomon12d22642019-01-29 14:38:50 -0500318 sk_sp<const GrBuffer> fVertexBuffer;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400319 int fFirstVertex;
320 int fVerticesInChunk;
321 SkPoint* fVertices;
322 SkPoint* fCurVert;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400323
Brian Salomon12d22642019-01-29 14:38:50 -0500324 sk_sp<const GrBuffer> fIndexBuffer;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400325 int fFirstIndex;
326 int fIndicesInChunk;
327 uint16_t* fIndices;
328 uint16_t* fCurIdx;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400329 uint16_t fSubpathIndexStart;
330};
egdanielaf18a092015-01-05 10:22:28 -0800331
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400332class DefaultPathOp final : public GrMeshDrawOp {
333private:
334 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
335
joshualitt332c7292015-02-23 08:44:31 -0800336public:
Brian Salomon25a88092016-12-01 09:36:50 -0500337 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700338
Robert Phillipsb97da532019-02-12 15:24:12 -0500339 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400340 GrPaint&& paint,
341 const SkPath& path,
342 SkScalar tolerance,
343 uint8_t coverage,
344 const SkMatrix& viewMatrix,
345 bool isHairline,
346 GrAAType aaType,
347 const SkRect& devBounds,
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400348 const GrUserStencilSettings* stencilSettings) {
Robert Phillips7c525e62018-06-12 10:11:12 -0400349 return Helper::FactoryHelper<DefaultPathOp>(context, std::move(paint), path, tolerance,
350 coverage, viewMatrix, isHairline, aaType,
351 devBounds, stencilSettings);
bsalomon@google.com30085192011-08-19 15:42:31 +0000352 }
353
Brian Salomon780dad12016-12-15 18:08:40 -0500354 const char* name() const override { return "DefaultPathOp"; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000355
Chris Dalton1706cbf2019-05-21 19:35:29 -0600356 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400357 fHelper.visitProxies(func);
358 }
359
Brian Osman9a390ac2018-11-12 09:47:48 -0500360#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -0500361 SkString dumpInfo() const override {
362 SkString string;
Brian Osmancf860852018-10-31 14:04:39 -0400363 string.appendf("Color: 0x%08x Count: %d\n", fColor.toBytes_RGBA(), fPaths.count());
Brian Salomon780dad12016-12-15 18:08:40 -0500364 for (const auto& path : fPaths) {
365 string.appendf("Tolerance: %.2f\n", path.fTolerance);
Brian Salomon7c3e7182016-12-01 09:35:30 -0500366 }
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400367 string += fHelper.dumpInfo();
368 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -0500369 return string;
370 }
Brian Osman9a390ac2018-11-12 09:47:48 -0500371#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -0500372
Brian Osmancf860852018-10-31 14:04:39 -0400373 DefaultPathOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color, const SkPath& path,
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400374 SkScalar tolerance, uint8_t coverage, const SkMatrix& viewMatrix, bool isHairline,
375 GrAAType aaType, const SkRect& devBounds,
376 const GrUserStencilSettings* stencilSettings)
Brian Salomon780dad12016-12-15 18:08:40 -0500377 : INHERITED(ClassID())
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400378 , fHelper(helperArgs, aaType, stencilSettings)
Brian Salomon780dad12016-12-15 18:08:40 -0500379 , fColor(color)
380 , fCoverage(coverage)
381 , fViewMatrix(viewMatrix)
382 , fIsHairline(isHairline) {
383 fPaths.emplace_back(PathData{path, tolerance});
joshualitt332c7292015-02-23 08:44:31 -0800384
Brian Salomon780dad12016-12-15 18:08:40 -0500385 this->setBounds(devBounds, HasAABloat::kNo,
386 isHairline ? IsZeroArea::kYes : IsZeroArea::kNo);
387 }
388
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400389 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
390
Brian Osman5ced0bf2019-03-15 10:15:29 -0400391 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
392 GrFSAAType fsaaType, GrClampType clampType) override {
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400393 GrProcessorAnalysisCoverage gpCoverage =
394 this->coverage() == 0xFF ? GrProcessorAnalysisCoverage::kNone
395 : GrProcessorAnalysisCoverage::kSingleChannel;
Brian Osman8fa7ab42019-03-18 10:22:42 -0400396 // This Op uses uniform (not vertex) color, so doesn't need to track wide color.
397 return fHelper.finalizeProcessors(
398 caps, clip, fsaaType, clampType, gpCoverage, &fColor, nullptr);
Brian Salomon92aee3d2016-12-21 09:20:25 -0500399 }
400
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400401private:
Brian Salomon91326c32017-08-09 16:02:19 -0400402 void onPrepareDraws(Target* target) override {
bungeman06ca8ec2016-06-09 08:01:03 -0700403 sk_sp<GrGeometryProcessor> gp;
joshualittdf0c5572015-08-03 11:35:28 -0700404 {
405 using namespace GrDefaultGeoProcFactory;
406 Color color(this->color());
407 Coverage coverage(this->coverage());
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400408 LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
409 : LocalCoords::kUnused_Type);
Ruiqi Maob609e6d2018-07-17 10:19:38 -0400410 gp = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
411 color,
412 coverage,
413 localCoords,
414 this->viewMatrix());
joshualittdf0c5572015-08-03 11:35:28 -0700415 }
joshualitt332c7292015-02-23 08:44:31 -0800416
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500417 SkASSERT(gp->vertexStride() == sizeof(SkPoint));
joshualitt332c7292015-02-23 08:44:31 -0800418
Brian Salomon763abf02018-05-01 18:49:38 +0000419 int instanceCount = fPaths.count();
420
421 // We avoid indices when we have a single hairline contour.
422 bool isIndexed = !this->isHairline() || instanceCount > 1 ||
423 PathGeoBuilder::PathHasMultipleSubpaths(fPaths[0].fPath);
Brian Osman7f95dfc2017-06-09 14:43:49 +0000424
425 // determine primitiveType
Brian Osman7f95dfc2017-06-09 14:43:49 +0000426 GrPrimitiveType primitiveType;
427 if (this->isHairline()) {
Brian Salomon763abf02018-05-01 18:49:38 +0000428 primitiveType = isIndexed ? GrPrimitiveType::kLines : GrPrimitiveType::kLineStrip;
Brian Osman7f95dfc2017-06-09 14:43:49 +0000429 } else {
Brian Salomon06c33042018-04-25 15:45:27 -0400430 primitiveType = GrPrimitiveType::kTriangles;
Brian Osman7f95dfc2017-06-09 14:43:49 +0000431 }
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700432 PathGeoBuilder pathGeoBuilder(primitiveType, target, std::move(gp));
Brian Osman7f95dfc2017-06-09 14:43:49 +0000433
434 // fill buffers
Brian Salomon763abf02018-05-01 18:49:38 +0000435 for (int i = 0; i < instanceCount; i++) {
436 const PathData& args = fPaths[i];
437 pathGeoBuilder.addPath(args.fPath, args.fTolerance);
Brian Osman7f95dfc2017-06-09 14:43:49 +0000438 }
joshualitt332c7292015-02-23 08:44:31 -0800439 }
440
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700441 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
442 fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
443 }
444
Brian Salomon7eae3e02018-08-07 14:02:38 +0000445 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon780dad12016-12-15 18:08:40 -0500446 DefaultPathOp* that = t->cast<DefaultPathOp>();
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400447 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000448 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -0700449 }
450
joshualitt332c7292015-02-23 08:44:31 -0800451 if (this->color() != that->color()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000452 return CombineResult::kCannotCombine;
joshualitt332c7292015-02-23 08:44:31 -0800453 }
454
455 if (this->coverage() != that->coverage()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000456 return CombineResult::kCannotCombine;
joshualitt332c7292015-02-23 08:44:31 -0800457 }
458
459 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000460 return CombineResult::kCannotCombine;
joshualitt332c7292015-02-23 08:44:31 -0800461 }
462
463 if (this->isHairline() != that->isHairline()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000464 return CombineResult::kCannotCombine;
joshualitt332c7292015-02-23 08:44:31 -0800465 }
466
Brian Salomon780dad12016-12-15 18:08:40 -0500467 fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
Brian Salomon7eae3e02018-08-07 14:02:38 +0000468 return CombineResult::kMerged;
joshualitt332c7292015-02-23 08:44:31 -0800469 }
470
Brian Osmancf860852018-10-31 14:04:39 -0400471 const SkPMColor4f& color() const { return fColor; }
Brian Salomon780dad12016-12-15 18:08:40 -0500472 uint8_t coverage() const { return fCoverage; }
Brian Salomon780dad12016-12-15 18:08:40 -0500473 const SkMatrix& viewMatrix() const { return fViewMatrix; }
474 bool isHairline() const { return fIsHairline; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000475
Brian Salomon780dad12016-12-15 18:08:40 -0500476 struct PathData {
bsalomon0432dd62016-06-30 07:19:27 -0700477 SkPath fPath;
478 SkScalar fTolerance;
479 };
480
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400481 SkSTArray<1, PathData, true> fPaths;
482 Helper fHelper;
Brian Osmancf860852018-10-31 14:04:39 -0400483 SkPMColor4f fColor;
Brian Salomon780dad12016-12-15 18:08:40 -0500484 uint8_t fCoverage;
485 SkMatrix fViewMatrix;
Brian Salomon780dad12016-12-15 18:08:40 -0500486 bool fIsHairline;
reed1b55a962015-09-17 20:16:13 -0700487
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400488 typedef GrMeshDrawOp INHERITED;
joshualitt332c7292015-02-23 08:44:31 -0800489};
bsalomon@google.com30085192011-08-19 15:42:31 +0000490
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400491} // anonymous namespace
492
Brian Osman11052242016-10-27 14:47:55 -0400493bool GrDefaultPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetContext,
Brian Salomon82f44312017-01-11 13:42:54 -0500494 GrPaint&& paint,
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500495 GrAAType aaType,
robertphillipsd2b6d642016-07-21 08:55:08 -0700496 const GrUserStencilSettings& userStencilSettings,
cdalton862cff32016-05-12 15:09:48 -0700497 const GrClip& clip,
joshualitt8059eb92014-12-29 15:10:07 -0800498 const SkMatrix& viewMatrix,
bsalomon8acedde2016-06-24 10:42:16 -0700499 const GrShape& shape,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000500 bool stencilOnly) {
Robert Phillipsb97da532019-02-12 15:24:12 -0500501 auto context = renderTargetContext->surfPriv().getContext();
Robert Phillips7c525e62018-06-12 10:11:12 -0400502
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500503 SkASSERT(GrAAType::kCoverage != aaType);
bsalomon8acedde2016-06-24 10:42:16 -0700504 SkPath path;
505 shape.asPath(&path);
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000506
507 SkScalar hairlineCoverage;
joshualitt2e3b3e32014-12-09 13:31:14 -0800508 uint8_t newCoverage = 0xff;
bsalomon6663acf2016-05-10 09:14:17 -0700509 bool isHairline = false;
bsalomon8acedde2016-06-24 10:42:16 -0700510 if (IsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) {
joshualitt2e3b3e32014-12-09 13:31:14 -0800511 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
bsalomon6663acf2016-05-10 09:14:17 -0700512 isHairline = true;
513 } else {
bsalomon8acedde2016-06-24 10:42:16 -0700514 SkASSERT(shape.style().isSimpleFill());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000515 }
516
cdalton93a379b2016-05-11 13:58:08 -0700517 int passCount = 0;
Brian Salomonf0861672017-05-08 11:10:10 -0400518 const GrUserStencilSettings* passes[2];
cdalton93a379b2016-05-11 13:58:08 -0700519 bool reverse = false;
520 bool lastPassIsBounds;
bsalomon@google.com30085192011-08-19 15:42:31 +0000521
joshualitt332c7292015-02-23 08:44:31 -0800522 if (isHairline) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000523 passCount = 1;
524 if (stencilOnly) {
525 passes[0] = &gDirectToStencil;
526 } else {
robertphillipsd2b6d642016-07-21 08:55:08 -0700527 passes[0] = &userStencilSettings;
bsalomon@google.com30085192011-08-19 15:42:31 +0000528 }
529 lastPassIsBounds = false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000530 } else {
bsalomon8acedde2016-06-24 10:42:16 -0700531 if (single_pass_shape(shape)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000532 passCount = 1;
533 if (stencilOnly) {
534 passes[0] = &gDirectToStencil;
535 } else {
robertphillipsd2b6d642016-07-21 08:55:08 -0700536 passes[0] = &userStencilSettings;
bsalomon@google.com30085192011-08-19 15:42:31 +0000537 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000538 lastPassIsBounds = false;
539 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000540 switch (path.getFillType()) {
sugoi@google.com12b4e272012-12-06 20:13:11 +0000541 case SkPath::kInverseEvenOdd_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000542 reverse = true;
543 // fallthrough
sugoi@google.com12b4e272012-12-06 20:13:11 +0000544 case SkPath::kEvenOdd_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000545 passes[0] = &gEOStencilPass;
546 if (stencilOnly) {
547 passCount = 1;
548 lastPassIsBounds = false;
549 } else {
550 passCount = 2;
551 lastPassIsBounds = true;
552 if (reverse) {
553 passes[1] = &gInvEOColorPass;
554 } else {
555 passes[1] = &gEOColorPass;
556 }
557 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000558 break;
559
sugoi@google.com12b4e272012-12-06 20:13:11 +0000560 case SkPath::kInverseWinding_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000561 reverse = true;
562 // fallthrough
sugoi@google.com12b4e272012-12-06 20:13:11 +0000563 case SkPath::kWinding_FillType:
Brian Salomon15b25092017-05-08 11:10:53 -0400564 passes[0] = &gWindStencilPass;
Brian Salomonf0861672017-05-08 11:10:10 -0400565 passCount = 2;
bsalomon@google.com30085192011-08-19 15:42:31 +0000566 if (stencilOnly) {
567 lastPassIsBounds = false;
568 --passCount;
569 } else {
570 lastPassIsBounds = true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000571 if (reverse) {
572 passes[passCount-1] = &gInvWindColorPass;
573 } else {
574 passes[passCount-1] = &gWindColorPass;
575 }
576 }
577 break;
578 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000579 SkDEBUGFAIL("Unknown path fFill!");
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000580 return false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000581 }
582 }
583 }
584
senorblanco2b4bb072015-04-22 13:45:18 -0700585 SkScalar tol = GrPathUtils::kDefaultTolerance;
joshualitt332c7292015-02-23 08:44:31 -0800586 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
587
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000588 SkRect devBounds;
Robert Phillipsec325342017-10-30 18:02:48 +0000589 GetPathDevBounds(path,
590 renderTargetContext->asRenderTargetProxy()->worstCaseWidth(),
591 renderTargetContext->asRenderTargetProxy()->worstCaseHeight(),
592 viewMatrix, &devBounds);
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000593
bsalomon@google.com30085192011-08-19 15:42:31 +0000594 for (int p = 0; p < passCount; ++p) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000595 if (lastPassIsBounds && (p == passCount-1)) {
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000596 SkRect bounds;
joshualittd27f73e2014-12-29 07:43:36 -0800597 SkMatrix localMatrix = SkMatrix::I();
bsalomon@google.com30085192011-08-19 15:42:31 +0000598 if (reverse) {
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000599 // draw over the dev bounds (which will be the whole dst surface for inv fill).
600 bounds = devBounds;
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000601 SkMatrix vmi;
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000602 // mapRect through persp matrix may not be correct
joshualitt8059eb92014-12-29 15:10:07 -0800603 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000604 vmi.mapRect(&bounds);
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000605 } else {
joshualittd27f73e2014-12-29 07:43:36 -0800606 if (!viewMatrix.invert(&localMatrix)) {
607 return false;
608 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000609 }
610 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000611 bounds = path.getBounds();
bsalomon@google.com30085192011-08-19 15:42:31 +0000612 }
joshualitt8059eb92014-12-29 15:10:07 -0800613 const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
614 viewMatrix;
Michael Ludwig72ab3462018-12-10 12:43:36 -0500615 // This is a non-coverage aa rect op since we assert aaType != kCoverage at the start
Mike Klein16885072018-12-11 09:54:31 -0500616 assert_alive(paint);
Michael Ludwigaa1b6b32019-05-29 14:43:13 -0400617 renderTargetContext->priv().stencilRect(clip, passes[p], std::move(paint),
618 GrAA(aaType == GrAAType::kMSAA), viewM, bounds, &localMatrix);
robertphillips976f5f02016-06-03 10:59:20 -0700619 } else {
Brian Salomond4652ca2017-01-13 12:11:36 -0500620 bool stencilPass = stencilOnly || passCount > 1;
Brian Salomonb74ef032017-08-10 12:46:01 -0400621 std::unique_ptr<GrDrawOp> op;
Brian Salomond4652ca2017-01-13 12:11:36 -0500622 if (stencilPass) {
Brian Salomonb74ef032017-08-10 12:46:01 -0400623 GrPaint stencilPaint;
624 stencilPaint.setXPFactory(GrDisableColorXPFactory::Get());
Robert Phillips7c525e62018-06-12 10:11:12 -0400625 op = DefaultPathOp::Make(context, std::move(stencilPaint), path, srcSpaceTol,
626 newCoverage, viewMatrix, isHairline, aaType, devBounds,
627 passes[p]);
Brian Salomonb74ef032017-08-10 12:46:01 -0400628 } else {
Mike Klein16885072018-12-11 09:54:31 -0500629 assert_alive(paint);
Robert Phillips7c525e62018-06-12 10:11:12 -0400630 op = DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, newCoverage,
Brian Salomonb74ef032017-08-10 12:46:01 -0400631 viewMatrix, isHairline, aaType, devBounds, passes[p]);
Brian Salomond4652ca2017-01-13 12:11:36 -0500632 }
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400633 renderTargetContext->addDrawOp(clip, std::move(op));
bsalomon@google.com30085192011-08-19 15:42:31 +0000634 }
635 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000636 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000637}
638
Chris Dalton5ed44232017-09-07 13:22:46 -0600639GrPathRenderer::CanDrawPath
640GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
Chris Dalton09e56892019-03-13 00:22:01 -0600641 bool isHairline = IsStrokeHairlineOrEquivalent(
642 args.fShape->style(), *args.fViewMatrix, nullptr);
Eric Karl5c779752017-05-08 12:02:07 -0700643 // If we aren't a single_pass_shape or hairline, we require stencil buffers.
Greg Danielbe7fc462019-01-03 16:40:42 -0500644 if (!(single_pass_shape(*args.fShape) || isHairline) &&
645 (args.fCaps->avoidStencilBuffers() || args.fTargetIsWrappedVkSecondaryCB)) {
Chris Dalton5ed44232017-09-07 13:22:46 -0600646 return CanDrawPath::kNo;
Eric Karl5c779752017-05-08 12:02:07 -0700647 }
Chris Dalton09e56892019-03-13 00:22:01 -0600648 // If antialiasing is required, we only support MSAA.
649 if (AATypeFlags::kNone != args.fAATypeFlags && !(AATypeFlags::kMSAA & args.fAATypeFlags)) {
650 return CanDrawPath::kNo;
651 }
652 // This can draw any path with any simple fill style.
653 if (!args.fShape->style().isSimpleFill() && !isHairline) {
Chris Dalton5ed44232017-09-07 13:22:46 -0600654 return CanDrawPath::kNo;
655 }
656 // This is the fallback renderer for when a path is too complicated for the others to draw.
657 return CanDrawPath::kAsBackup;
bsalomon@google.com30085192011-08-19 15:42:31 +0000658}
659
bsalomon0aff2fa2015-07-31 06:48:27 -0700660bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400661 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700662 "GrDefaultPathRenderer::onDrawPath");
Chris Dalton09e56892019-03-13 00:22:01 -0600663 GrAAType aaType = (AATypeFlags::kNone != args.fAATypeFlags)
664 ? GrAAType::kMSAA
665 : GrAAType::kNone;
666
667 return this->internalDrawPath(
668 args.fRenderTargetContext, std::move(args.fPaint), aaType, *args.fUserStencilSettings,
669 *args.fClip, *args.fViewMatrix, *args.fShape, false);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000670}
671
bsalomon0aff2fa2015-07-31 06:48:27 -0700672void GrDefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400673 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700674 "GrDefaultPathRenderer::onStencilPath");
bsalomon8acedde2016-06-24 10:42:16 -0700675 SkASSERT(!args.fShape->inverseFilled());
robertphillips976f5f02016-06-03 10:59:20 -0700676
677 GrPaint paint;
Brian Salomona1633922017-01-09 11:46:10 -0500678 paint.setXPFactory(GrDisableColorXPFactory::Get());
robertphillips976f5f02016-06-03 10:59:20 -0700679
Chris Dalton09e56892019-03-13 00:22:01 -0600680 auto aaType = (GrAA::kYes == args.fDoStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
681
682 this->internalDrawPath(
683 args.fRenderTargetContext, std::move(paint), aaType, GrUserStencilSettings::kUnused,
684 *args.fClip, *args.fViewMatrix, *args.fShape, true);
bsalomon@google.com30085192011-08-19 15:42:31 +0000685}
joshualitt622d3ad2015-05-07 08:13:11 -0700686
687///////////////////////////////////////////////////////////////////////////////////////////////////
688
Hal Canary6f6961e2017-01-31 13:50:44 -0500689#if GR_TEST_UTILS
joshualitt622d3ad2015-05-07 08:13:11 -0700690
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400691GR_DRAW_OP_TEST_DEFINE(DefaultPathOp) {
joshualitt622d3ad2015-05-07 08:13:11 -0700692 SkMatrix viewMatrix = GrTest::TestMatrix(random);
693
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500694 // For now just hairlines because the other types of draws require two ops.
695 // TODO we should figure out a way to combine the stencil and cover steps into one op.
bsalomon6663acf2016-05-10 09:14:17 -0700696 GrStyle style(SkStrokeRec::kHairline_InitStyle);
joshualitt622d3ad2015-05-07 08:13:11 -0700697 SkPath path = GrTest::TestPath(random);
698
699 // Compute srcSpaceTol
700 SkRect bounds = path.getBounds();
701 SkScalar tol = GrPathUtils::kDefaultTolerance;
702 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
703
joshualitt622d3ad2015-05-07 08:13:11 -0700704 viewMatrix.mapRect(&bounds);
705 uint8_t coverage = GrRandomCoverage(random);
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400706 GrAAType aaType = GrAAType::kNone;
707 if (GrFSAAType::kUnifiedMSAA == fsaaType && random->nextBool()) {
708 aaType = GrAAType::kMSAA;
709 }
Robert Phillips7c525e62018-06-12 10:11:12 -0400710 return DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, coverage, viewMatrix,
711 true, aaType, bounds, GrGetRandomStencil(random, context));
joshualitt622d3ad2015-05-07 08:13:11 -0700712}
713
714#endif