blob: 4d028b357c4601a30ef1fa4db137f9e4feab7719 [file] [log] [blame]
Chris Daltonb832ce62020-01-06 19:49:37 -07001/*
2 * Copyright 2019 Google LLC.
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 "src/gpu/tessellate/GrTessellatePathOp.h"
9
Chris Daltond081dce2020-01-23 12:09:04 -070010#include "src/gpu/GrEagerVertexAllocator.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070011#include "src/gpu/GrGpu.h"
12#include "src/gpu/GrOpFlushState.h"
Chris Dalton17dc4182020-03-25 16:18:16 -060013#include "src/gpu/GrTriangulator.h"
Chris Dalton4328e922020-01-29 13:16:14 -070014#include "src/gpu/tessellate/GrFillPathShader.h"
Chris Daltonf5132a02020-04-27 23:40:03 -060015#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
Chris Dalton42915c22020-04-22 16:24:43 -060016#include "src/gpu/tessellate/GrMidpointContourParser.h"
Chris Daltonb5391d92020-05-24 14:55:54 -060017#include "src/gpu/tessellate/GrResolveLevelCounter.h"
Chris Daltonf9aea7f2020-01-21 11:19:26 -070018#include "src/gpu/tessellate/GrStencilPathShader.h"
Chris Daltonb96995d2020-06-04 16:44:29 -060019#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070020
Chris Daltonb96995d2020-06-04 16:44:29 -060021constexpr static float kLinearizationIntolerance =
22 GrTessellationPathRenderer::kLinearizationIntolerance;
23
24constexpr static int kMaxResolveLevel = GrTessellationPathRenderer::kMaxResolveLevel;
25
26using OpFlags = GrTessellationPathRenderer::OpFlags;
Chris Daltonb5391d92020-05-24 14:55:54 -060027
Chris Daltonb832ce62020-01-06 19:49:37 -070028GrTessellatePathOp::FixedFunctionFlags GrTessellatePathOp::fixedFunctionFlags() const {
29 auto flags = FixedFunctionFlags::kUsesStencil;
30 if (GrAAType::kNone != fAAType) {
31 flags |= FixedFunctionFlags::kUsesHWAA;
32 }
33 return flags;
34}
35
Robert Phillipsc655c3a2020-03-18 13:23:45 -040036void GrTessellatePathOp::onPrePrepare(GrRecordingContext*,
Brian Salomon8afde5f2020-04-01 16:22:00 -040037 const GrSurfaceProxyView* writeView,
Robert Phillipsc655c3a2020-03-18 13:23:45 -040038 GrAppliedClip*,
39 const GrXferProcessor::DstProxyView&) {
40}
41
Chris Daltonb5391d92020-05-24 14:55:54 -060042void GrTessellatePathOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton4328e922020-01-29 13:16:14 -070043 int numVerbs = fPath.countVerbs();
44 if (numVerbs <= 0) {
45 return;
46 }
Chris Daltonb5391d92020-05-24 14:55:54 -060047
48 // First check if the path is large and/or simple enough that we can actually triangulate the
49 // inner polygon(s) on the CPU. This is our fastest approach. It allows us to stencil only the
50 // curves, and then fill the internal polygons directly to the final render target, thus drawing
51 // the majority of pixels in a single render pass.
52 SkScalar scales[2];
53 SkAssertResult(fViewMatrix.getMinMaxScales(scales)); // Will fail if perspective.
54 const SkRect& bounds = fPath.getBounds();
Chris Dalton4328e922020-01-29 13:16:14 -070055 float gpuFragmentWork = bounds.height() * scales[0] * bounds.width() * scales[1];
56 float cpuTessellationWork = (float)numVerbs * SkNextLog2(numVerbs); // N log N.
57 if (cpuTessellationWork * 500 + (256 * 256) < gpuFragmentWork) { // Don't try below 256x256.
Chris Daltonb5391d92020-05-24 14:55:54 -060058 int numCountedCubics;
Chris Dalton04f9cda2020-04-23 10:04:25 -060059 // This will fail if the inner triangles do not form a simple polygon (e.g., self
60 // intersection, double winding).
Chris Daltonb5391d92020-05-24 14:55:54 -060061 if (this->prepareNonOverlappingInnerTriangles(flushState, &numCountedCubics)) {
62 if (!numCountedCubics) {
63 return;
64 }
65 // Always use indirect draws for cubics instead of tessellation here. Our goal in this
66 // mode is to maximize GPU performance, and the middle-out topology used by our indirect
67 // draws is easier on the rasterizer than a tessellated fan. There also seems to be a
68 // small amount of fixed tessellation overhead that this avoids.
Chris Daltonb5391d92020-05-24 14:55:54 -060069 GrResolveLevelCounter resolveLevelCounter;
Chris Daltonb96995d2020-06-04 16:44:29 -060070 resolveLevelCounter.reset(fPath, fViewMatrix, kLinearizationIntolerance);
Chris Daltonb5391d92020-05-24 14:55:54 -060071 this->prepareIndirectOuterCubics(flushState, resolveLevelCounter);
Chris Dalton4328e922020-01-29 13:16:14 -070072 return;
73 }
74 }
75
Chris Daltonb5391d92020-05-24 14:55:54 -060076 // When there are only a few verbs, it seems to always be fastest to make a single indirect draw
77 // that contains both the inner triangles and the outer cubics, instead of using hardware
78 // tessellation. Also take this path if tessellation is not supported.
79 bool drawTrianglesAsIndirectCubicDraw = (numVerbs < 50);
Chris Daltonb96995d2020-06-04 16:44:29 -060080 if (drawTrianglesAsIndirectCubicDraw || (fOpFlags & OpFlags::kDisableHWTessellation)) {
Chris Daltonb5391d92020-05-24 14:55:54 -060081 // Prepare outer cubics with indirect draws.
82 GrResolveLevelCounter resolveLevelCounter;
83 this->prepareMiddleOutTrianglesAndCubics(flushState, &resolveLevelCounter,
84 drawTrianglesAsIndirectCubicDraw);
85 return;
86 }
87
Chris Daltonb96995d2020-06-04 16:44:29 -060088 // The caller should have sent Flags::kDisableHWTessellation if it was not supported.
89 SkASSERT(flushState->caps().shaderCaps()->tessellationSupport());
90
Chris Daltonb5391d92020-05-24 14:55:54 -060091 // Next see if we can split up the inner triangles and outer cubics into two draw calls. This
92 // allows for a more efficient inner triangle topology that can reduce the rasterizer load by a
93 // large margin on complex paths, but also causes greater CPU overhead due to the extra shader
94 // switches and draw calls.
Chris Dalton4328e922020-01-29 13:16:14 -070095 // NOTE: Raster-edge work is 1-dimensional, so we sum height and width instead of multiplying.
96 float rasterEdgeWork = (bounds.height() + bounds.width()) * scales[1] * fPath.countVerbs();
Chris Daltonb5391d92020-05-24 14:55:54 -060097 if (rasterEdgeWork > 300 * 300) {
98 this->prepareMiddleOutTrianglesAndCubics(flushState);
Chris Daltonf9aea7f2020-01-21 11:19:26 -070099 return;
100 }
101
102 // Fastest CPU approach: emit one cubic wedge per verb, fanning out from the center.
Chris Daltonb5391d92020-05-24 14:55:54 -0600103 this->prepareTessellatedCubicWedges(flushState);
Chris Daltonb832ce62020-01-06 19:49:37 -0700104}
105
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600106bool GrTessellatePathOp::prepareNonOverlappingInnerTriangles(GrMeshDrawOp::Target* target,
Chris Daltonf5132a02020-04-27 23:40:03 -0600107 int* numCountedCurves) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600108 SkASSERT(!fTriangleBuffer);
109 SkASSERT(!fDoStencilTriangleBuffer);
110 SkASSERT(!fDoFillTriangleBuffer);
111
112 using GrTriangulator::Mode;
113
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600114 GrEagerDynamicVertexAllocator vertexAlloc(target, &fTriangleBuffer, &fBaseTriangleVertex);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600115 fTriangleVertexCount = GrTriangulator::PathToTriangles(fPath, 0, SkRect::MakeEmpty(),
116 &vertexAlloc, Mode::kSimpleInnerPolygons,
117 numCountedCurves);
118 if (fTriangleVertexCount == 0) {
119 // Mode::kSimpleInnerPolygons causes PathToTriangles to fail if the inner polygon(s) are not
120 // simple.
121 return false;
122 }
Chris Daltonb96995d2020-06-04 16:44:29 -0600123 if (((OpFlags::kStencilOnly | OpFlags::kWireframe) & fOpFlags) ||
124 GrAAType::kCoverage == fAAType ||
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600125 (target->appliedClip() && target->appliedClip()->hasStencilClip())) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600126 // If we have certain flags, mixed samples, or a stencil clip then we unfortunately
127 // can't fill the inner polygon directly. Indicate that these triangles need to be
128 // stencilled.
129 fDoStencilTriangleBuffer = true;
130 }
Chris Daltonb96995d2020-06-04 16:44:29 -0600131 if (!(OpFlags::kStencilOnly & fOpFlags)) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600132 fDoFillTriangleBuffer = true;
133 }
134 return true;
135}
136
Chris Daltonb5391d92020-05-24 14:55:54 -0600137void GrTessellatePathOp::prepareMiddleOutTrianglesAndCubics(
138 GrMeshDrawOp::Target* target, GrResolveLevelCounter* resolveLevelCounter,
139 bool drawTrianglesAsIndirectCubicDraw) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600140 SkASSERT(!fTriangleBuffer);
141 SkASSERT(!fDoStencilTriangleBuffer);
142 SkASSERT(!fDoFillTriangleBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600143 SkASSERT(!fCubicBuffer);
144 SkASSERT(!fStencilCubicsShader);
145 SkASSERT(!fIndirectDrawBuffer);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600146
Chris Daltonf5132a02020-04-27 23:40:03 -0600147 // No initial moveTo, plus an implicit close at the end; n-2 triangles fill an n-gon.
Chris Daltonb5391d92020-05-24 14:55:54 -0600148 int maxInnerTriangles = fPath.countVerbs() - 1;
149 int maxCubics = fPath.countVerbs();
Chris Dalton42915c22020-04-22 16:24:43 -0600150
Chris Daltonb5391d92020-05-24 14:55:54 -0600151 SkPoint* vertexData;
152 int vertexAdvancePerTriangle;
153 if (drawTrianglesAsIndirectCubicDraw) {
154 // Allocate the triangles as 4-point instances at the beginning of the cubic buffer.
155 SkASSERT(resolveLevelCounter);
156 vertexAdvancePerTriangle = 4;
157 int baseTriangleInstance;
158 vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
159 sizeof(SkPoint) * 4, maxInnerTriangles + maxCubics, &fCubicBuffer,
160 &baseTriangleInstance));
161 fBaseCubicVertex = baseTriangleInstance * 4;
162 } else {
163 // Allocate the triangles as normal 3-point instances in the triangle buffer.
164 vertexAdvancePerTriangle = 3;
165 vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
166 sizeof(SkPoint), maxInnerTriangles * 3, &fTriangleBuffer, &fBaseTriangleVertex));
167 }
Chris Dalton42915c22020-04-22 16:24:43 -0600168 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600169 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600170 }
Chris Dalton42915c22020-04-22 16:24:43 -0600171
Chris Daltonb5391d92020-05-24 14:55:54 -0600172 GrVectorXform xform(fViewMatrix);
173 GrMiddleOutPolygonTriangulator middleOut(vertexData, vertexAdvancePerTriangle,
174 fPath.countVerbs());
175 if (resolveLevelCounter) {
176 resolveLevelCounter->reset();
177 }
178 int numCountedCurves = 0;
Chris Daltonf7a33072020-05-01 10:33:08 -0600179 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
180 switch (verb) {
181 case SkPathVerb::kMove:
Chris Daltonb5391d92020-05-24 14:55:54 -0600182 middleOut.closeAndMove(pts[0]);
Chris Daltonf5132a02020-04-27 23:40:03 -0600183 break;
Chris Daltonf7a33072020-05-01 10:33:08 -0600184 case SkPathVerb::kLine:
185 middleOut.pushVertex(pts[1]);
186 break;
187 case SkPathVerb::kQuad:
188 middleOut.pushVertex(pts[2]);
Chris Daltonb5391d92020-05-24 14:55:54 -0600189 if (resolveLevelCounter) {
190 // Quadratics get converted to cubics before rendering.
191 resolveLevelCounter->countCubic(GrWangsFormula::quadratic_log2(
Chris Daltonb96995d2020-06-04 16:44:29 -0600192 kLinearizationIntolerance, pts, xform));
Chris Daltonb5391d92020-05-24 14:55:54 -0600193 break;
194 }
195 ++numCountedCurves;
Chris Daltonf7a33072020-05-01 10:33:08 -0600196 break;
197 case SkPathVerb::kCubic:
198 middleOut.pushVertex(pts[3]);
Chris Daltonb5391d92020-05-24 14:55:54 -0600199 if (resolveLevelCounter) {
200 resolveLevelCounter->countCubic(GrWangsFormula::cubic_log2(
Chris Daltonb96995d2020-06-04 16:44:29 -0600201 kLinearizationIntolerance, pts, xform));
Chris Daltonb5391d92020-05-24 14:55:54 -0600202 break;
203 }
204 ++numCountedCurves;
Chris Daltonf7a33072020-05-01 10:33:08 -0600205 break;
206 case SkPathVerb::kClose:
Chris Daltonf5132a02020-04-27 23:40:03 -0600207 middleOut.close();
208 break;
Chris Daltonf7a33072020-05-01 10:33:08 -0600209 case SkPathVerb::kConic:
Chris Daltonf7a33072020-05-01 10:33:08 -0600210 SkUNREACHABLE;
Chris Daltonf5132a02020-04-27 23:40:03 -0600211 }
Chris Dalton42915c22020-04-22 16:24:43 -0600212 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600213 int triangleCount = middleOut.close();
214 SkASSERT(triangleCount <= maxInnerTriangles);
Chris Dalton42915c22020-04-22 16:24:43 -0600215
Chris Daltonb5391d92020-05-24 14:55:54 -0600216 if (drawTrianglesAsIndirectCubicDraw) {
217 SkASSERT(resolveLevelCounter);
218 int totalInstanceCount = triangleCount + resolveLevelCounter->totalCubicInstanceCount();
219 SkASSERT(vertexAdvancePerTriangle == 4);
220 target->putBackVertices(maxInnerTriangles + maxCubics - totalInstanceCount,
221 sizeof(SkPoint) * 4);
222 if (totalInstanceCount) {
223 this->prepareIndirectOuterCubicsAndTriangles(target, *resolveLevelCounter, vertexData,
224 triangleCount);
225 }
226 } else {
227 SkASSERT(vertexAdvancePerTriangle == 3);
228 target->putBackVertices(maxInnerTriangles - triangleCount, sizeof(SkPoint) * 3);
229 fTriangleVertexCount = triangleCount * 3;
230 if (fTriangleVertexCount) {
231 fDoStencilTriangleBuffer = true;
232 }
233 if (resolveLevelCounter) {
234 this->prepareIndirectOuterCubics(target, *resolveLevelCounter);
235 } else {
236 this->prepareTessellatedOuterCubics(target, numCountedCurves);
237 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600238 }
Chris Dalton42915c22020-04-22 16:24:43 -0600239}
240
241static SkPoint lerp(const SkPoint& a, const SkPoint& b, float T) {
242 SkASSERT(1 != T); // The below does not guarantee lerp(a, b, 1) === b.
243 return (b - a) * T + a;
244}
245
Chris Daltonf7a33072020-05-01 10:33:08 -0600246static void line2cubic(const SkPoint& p0, const SkPoint& p1, SkPoint* out) {
247 out[0] = p0;
248 out[1] = lerp(p0, p1, 1/3.f);
249 out[2] = lerp(p0, p1, 2/3.f);
250 out[3] = p1;
Chris Dalton42915c22020-04-22 16:24:43 -0600251}
252
Chris Daltonf7a33072020-05-01 10:33:08 -0600253static void quad2cubic(const SkPoint pts[], SkPoint* out) {
254 out[0] = pts[0];
255 out[1] = lerp(pts[0], pts[1], 2/3.f);
256 out[2] = lerp(pts[1], pts[2], 1/3.f);
257 out[3] = pts[2];
Chris Dalton42915c22020-04-22 16:24:43 -0600258}
259
Chris Daltonb5391d92020-05-24 14:55:54 -0600260void GrTessellatePathOp::prepareIndirectOuterCubics(
261 GrMeshDrawOp::Target* target, const GrResolveLevelCounter& resolveLevelCounter) {
262 SkASSERT(resolveLevelCounter.totalCubicInstanceCount() >= 0);
263 if (resolveLevelCounter.totalCubicInstanceCount() == 0) {
264 return;
265 }
266 // Allocate a buffer to store the cubic data.
267 SkPoint* cubicData;
268 int baseInstance;
269 cubicData = static_cast<SkPoint*>(target->makeVertexSpace(
270 sizeof(SkPoint) * 4, resolveLevelCounter.totalCubicInstanceCount(), &fCubicBuffer,
271 &baseInstance));
272 if (!cubicData) {
273 return;
274 }
275 fBaseCubicVertex = baseInstance * 4;
276 this->prepareIndirectOuterCubicsAndTriangles(target, resolveLevelCounter, cubicData,
277 /*numTrianglesAtBeginningOfData=*/0);
278}
279
280void GrTessellatePathOp::prepareIndirectOuterCubicsAndTriangles(
281 GrMeshDrawOp::Target* target, const GrResolveLevelCounter& resolveLevelCounter,
282 SkPoint* cubicData, int numTrianglesAtBeginningOfData) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600283 SkASSERT(target->caps().drawInstancedSupport());
Chris Daltonb5391d92020-05-24 14:55:54 -0600284 SkASSERT(numTrianglesAtBeginningOfData + resolveLevelCounter.totalCubicInstanceCount() > 0);
285 SkASSERT(!fStencilCubicsShader);
286 SkASSERT(cubicData);
287
Chris Dalton1443c9d2020-05-27 09:43:34 -0600288 fIndirectIndexBuffer = GrMiddleOutCubicShader::FindOrMakeMiddleOutIndexBuffer(
289 target->resourceProvider());
290 if (!fIndirectIndexBuffer) {
291 return;
292 }
293
Chris Daltonb5391d92020-05-24 14:55:54 -0600294 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with the base
295 // vertex on an instance boundary in order to accommodate this.
296 SkASSERT(fBaseCubicVertex % 4 == 0);
297 int baseInstance = fBaseCubicVertex >> 2;
298
299 // Start preparing the indirect draw buffer.
300 fIndirectDrawCount = resolveLevelCounter.totalCubicIndirectDrawCount();
301 if (numTrianglesAtBeginningOfData) {
302 ++fIndirectDrawCount; // Add an indirect draw for the triangles at the beginning.
303 }
304
305 // Allocate space for the GrDrawIndexedIndirectCommand structs.
306 GrDrawIndexedIndirectCommand* indirectData = target->makeDrawIndexedIndirectSpace(
307 fIndirectDrawCount, &fIndirectDrawBuffer, &fIndirectDrawOffset);
308 if (!indirectData) {
309 SkASSERT(!fIndirectDrawBuffer);
310 return;
311 }
312
313 // Fill out the GrDrawIndexedIndirectCommand structs and determine the starting instance data
314 // location at each resolve level.
315 SkPoint* instanceLocations[kMaxResolveLevel + 1];
316 int indirectIdx = 0;
317 int runningInstanceCount = 0;
318 if (numTrianglesAtBeginningOfData) {
319 // The caller has already packed "triangleInstanceCount" triangles into 4-point instances
320 // at the beginning of the instance buffer. Add a special-case indirect draw here that will
321 // emit the triangles [P0, P1, P2] from these 4-point instances.
322 indirectData[0] = GrMiddleOutCubicShader::MakeDrawTrianglesIndirectCmd(
323 numTrianglesAtBeginningOfData, baseInstance);
324 indirectIdx = 1;
325 runningInstanceCount = numTrianglesAtBeginningOfData;
326 }
327 for (int resolveLevel = 1; resolveLevel <= kMaxResolveLevel; ++resolveLevel) {
328 instanceLocations[resolveLevel] = cubicData + runningInstanceCount * 4;
329 if (int instanceCountAtCurrLevel = resolveLevelCounter[resolveLevel]) {
330 indirectData[indirectIdx++] = GrMiddleOutCubicShader::MakeDrawCubicsIndirectCmd(
331 resolveLevel, instanceCountAtCurrLevel, baseInstance + runningInstanceCount);
332 runningInstanceCount += instanceCountAtCurrLevel;
333 }
334 }
335
336#ifdef SK_DEBUG
337 SkASSERT(indirectIdx == fIndirectDrawCount);
338 SkASSERT(runningInstanceCount == numTrianglesAtBeginningOfData +
339 resolveLevelCounter.totalCubicInstanceCount());
340 SkASSERT(fIndirectDrawCount > 0);
341
342 SkPoint* endLocations[kMaxResolveLevel + 1];
343 memcpy(endLocations, instanceLocations + 1, kMaxResolveLevel * sizeof(SkPoint*));
344 int totalInstanceCount = numTrianglesAtBeginningOfData +
345 resolveLevelCounter.totalCubicInstanceCount();
346 endLocations[kMaxResolveLevel] = cubicData + totalInstanceCount * 4;
347#endif
348
349 fCubicVertexCount = numTrianglesAtBeginningOfData * 4;
350
351 if (resolveLevelCounter.totalCubicInstanceCount()) {
352 GrVectorXform xform(fViewMatrix);
353 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
354 int level;
355 switch (verb) {
356 default:
357 continue;
358 case SkPathVerb::kQuad:
Chris Daltonb96995d2020-06-04 16:44:29 -0600359 level = GrWangsFormula::quadratic_log2(kLinearizationIntolerance, pts, xform);
Chris Daltonb5391d92020-05-24 14:55:54 -0600360 if (level == 0) {
361 continue;
362 }
363 level = std::min(level, kMaxResolveLevel);
364 quad2cubic(pts, instanceLocations[level]);
365 break;
366 case SkPathVerb::kCubic:
Chris Daltonb96995d2020-06-04 16:44:29 -0600367 level = GrWangsFormula::cubic_log2(kLinearizationIntolerance, pts, xform);
Chris Daltonb5391d92020-05-24 14:55:54 -0600368 if (level == 0) {
369 continue;
370 }
371 level = std::min(level, kMaxResolveLevel);
372 memcpy(instanceLocations[level], pts, sizeof(SkPoint) * 4);
373 break;
374 }
375 instanceLocations[level] += 4;
376 fCubicVertexCount += 4;
377 }
378 }
379
380#ifdef SK_DEBUG
381 for (int i = 1; i <= kMaxResolveLevel; ++i) {
382 SkASSERT(instanceLocations[i] == endLocations[i]);
383 }
384 SkASSERT(fCubicVertexCount == (numTrianglesAtBeginningOfData +
385 resolveLevelCounter.totalCubicInstanceCount()) * 4);
386#endif
387
388 fStencilCubicsShader = target->allocator()->make<GrMiddleOutCubicShader>(fViewMatrix);
389}
390
391void GrTessellatePathOp::prepareTessellatedOuterCubics(GrMeshDrawOp::Target* target,
392 int numCountedCurves) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600393 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
Chris Daltonb5391d92020-05-24 14:55:54 -0600394 SkASSERT(numCountedCurves >= 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600395 SkASSERT(!fCubicBuffer);
396 SkASSERT(!fStencilCubicsShader);
Chris Dalton42915c22020-04-22 16:24:43 -0600397
398 if (numCountedCurves == 0) {
399 return;
400 }
401
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600402 auto* vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
Chris Daltonb5391d92020-05-24 14:55:54 -0600403 sizeof(SkPoint), numCountedCurves * 4, &fCubicBuffer, &fBaseCubicVertex));
Chris Dalton04f9cda2020-04-23 10:04:25 -0600404 if (!vertexData) {
Chris Dalton42915c22020-04-22 16:24:43 -0600405 return;
406 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600407 fCubicVertexCount = 0;
Chris Dalton42915c22020-04-22 16:24:43 -0600408
Chris Daltonf7a33072020-05-01 10:33:08 -0600409 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
410 switch (verb) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600411 default:
412 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600413 case SkPathVerb::kQuad:
414 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
415 quad2cubic(pts, vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600416 break;
417 case SkPathVerb::kCubic:
418 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
419 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
Chris Daltonf7a33072020-05-01 10:33:08 -0600420 break;
Chris Dalton42915c22020-04-22 16:24:43 -0600421 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600422 fCubicVertexCount += 4;
Chris Dalton42915c22020-04-22 16:24:43 -0600423 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600424 SkASSERT(fCubicVertexCount == numCountedCurves * 4);
425
Chris Daltonb5391d92020-05-24 14:55:54 -0600426 fStencilCubicsShader = target->allocator()->make<GrTessellateCubicShader>(fViewMatrix);
Chris Dalton42915c22020-04-22 16:24:43 -0600427}
428
Chris Daltonb5391d92020-05-24 14:55:54 -0600429void GrTessellatePathOp::prepareTessellatedCubicWedges(GrMeshDrawOp::Target* target) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600430 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
Chris Dalton04f9cda2020-04-23 10:04:25 -0600431 SkASSERT(!fCubicBuffer);
432 SkASSERT(!fStencilCubicsShader);
433
Chris Dalton42915c22020-04-22 16:24:43 -0600434 // No initial moveTo, one wedge per verb, plus an implicit close at the end.
435 // Each wedge has 5 vertices.
436 int maxVertices = (fPath.countVerbs() + 1) * 5;
437
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600438 GrEagerDynamicVertexAllocator vertexAlloc(target, &fCubicBuffer, &fBaseCubicVertex);
Chris Dalton42915c22020-04-22 16:24:43 -0600439 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
440 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600441 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600442 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600443 fCubicVertexCount = 0;
Chris Dalton42915c22020-04-22 16:24:43 -0600444
445 GrMidpointContourParser parser(fPath);
446 while (parser.parseNextContour()) {
Chris Daltonf7a33072020-05-01 10:33:08 -0600447 SkPoint midpoint = parser.currentMidpoint();
448 SkPoint startPoint = {0, 0};
449 SkPoint lastPoint = startPoint;
450 for (auto [verb, pts, w] : parser.currentContour()) {
451 switch (verb) {
452 case SkPathVerb::kMove:
453 startPoint = lastPoint = pts[0];
Chris Dalton42915c22020-04-22 16:24:43 -0600454 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600455 case SkPathVerb::kClose:
456 continue; // Ignore. We can assume an implicit close at the end.
Chris Dalton42915c22020-04-22 16:24:43 -0600457 case SkPathVerb::kLine:
Chris Daltonf7a33072020-05-01 10:33:08 -0600458 line2cubic(pts[0], pts[1], vertexData + fCubicVertexCount);
459 lastPoint = pts[1];
Chris Dalton42915c22020-04-22 16:24:43 -0600460 break;
461 case SkPathVerb::kQuad:
Chris Daltonf7a33072020-05-01 10:33:08 -0600462 quad2cubic(pts, vertexData + fCubicVertexCount);
463 lastPoint = pts[2];
Chris Dalton42915c22020-04-22 16:24:43 -0600464 break;
465 case SkPathVerb::kCubic:
Chris Daltonf7a33072020-05-01 10:33:08 -0600466 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
467 lastPoint = pts[3];
Chris Dalton42915c22020-04-22 16:24:43 -0600468 break;
469 case SkPathVerb::kConic:
470 SkUNREACHABLE;
471 }
Chris Daltonf7a33072020-05-01 10:33:08 -0600472 vertexData[fCubicVertexCount + 4] = midpoint;
473 fCubicVertexCount += 5;
474 }
475 if (lastPoint != startPoint) {
476 line2cubic(lastPoint, startPoint, vertexData + fCubicVertexCount);
477 vertexData[fCubicVertexCount + 4] = midpoint;
Chris Dalton04f9cda2020-04-23 10:04:25 -0600478 fCubicVertexCount += 5;
Chris Dalton42915c22020-04-22 16:24:43 -0600479 }
480 }
481
Chris Dalton04f9cda2020-04-23 10:04:25 -0600482 vertexAlloc.unlock(fCubicVertexCount);
483
484 if (fCubicVertexCount) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600485 fStencilCubicsShader = target->allocator()->make<GrTessellateWedgeShader>(fViewMatrix);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600486 }
Chris Dalton42915c22020-04-22 16:24:43 -0600487}
488
Chris Daltonb5391d92020-05-24 14:55:54 -0600489void GrTessellatePathOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
490 this->drawStencilPass(flushState);
Chris Daltonb96995d2020-06-04 16:44:29 -0600491 if (!(OpFlags::kStencilOnly & fOpFlags)) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600492 this->drawCoverPass(flushState);
Chris Daltonb832ce62020-01-06 19:49:37 -0700493 }
494}
495
Chris Daltonb5391d92020-05-24 14:55:54 -0600496void GrTessellatePathOp::drawStencilPass(GrOpFlushState* flushState) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700497 // Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
498 constexpr static GrUserStencilSettings kIncrDecrStencil(
499 GrUserStencilSettings::StaticInitSeparate<
500 0x0000, 0x0000,
501 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
502 0xffff, 0xffff,
503 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
504 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
505 0xffff, 0xffff>());
506
507 // Inverts the bottom stencil bit. Used for "even/odd" fill.
508 constexpr static GrUserStencilSettings kInvertStencil(
509 GrUserStencilSettings::StaticInit<
510 0x0000,
511 GrUserStencilTest::kAlwaysIfInClip,
512 0xffff,
513 GrUserStencilOp::kInvert,
514 GrUserStencilOp::kKeep,
515 0x0001>());
516
517 GrPipeline::InitArgs initArgs;
518 if (GrAAType::kNone != fAAType) {
519 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
520 }
Chris Daltonb96995d2020-06-04 16:44:29 -0600521 if (flushState->caps().wireframeSupport() && (OpFlags::kWireframe & fOpFlags)) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700522 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
523 }
524 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
525 SkPathFillType::kEvenOdd == fPath.getFillType());
526 initArgs.fUserStencil = (SkPathFillType::kWinding == fPath.getFillType()) ?
527 &kIncrDecrStencil : &kInvertStencil;
Chris Daltonb5391d92020-05-24 14:55:54 -0600528 initArgs.fCaps = &flushState->caps();
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600529 GrPipeline pipeline(initArgs, GrDisableColorXPFactory::MakeXferProcessor(),
Chris Daltonb5391d92020-05-24 14:55:54 -0600530 flushState->appliedHardClip());
Chris Dalton012f8492020-03-05 11:49:15 -0700531
Chris Dalton04f9cda2020-04-23 10:04:25 -0600532 if (fDoStencilTriangleBuffer) {
533 SkASSERT(fTriangleBuffer);
534 GrStencilTriangleShader stencilTriangleShader(fViewMatrix);
Chris Daltonb5391d92020-05-24 14:55:54 -0600535 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
Chris Dalton04f9cda2020-04-23 10:04:25 -0600536 &stencilTriangleShader);
Chris Daltonb5391d92020-05-24 14:55:54 -0600537 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
538 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer.get());
539 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700540 }
541
Chris Dalton04f9cda2020-04-23 10:04:25 -0600542 if (fStencilCubicsShader) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600543 SkASSERT(fCubicBuffer);
544 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
545 fStencilCubicsShader);
546 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
547 if (fIndirectDrawBuffer) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600548 SkASSERT(fIndirectIndexBuffer);
549 flushState->bindBuffers(fIndirectIndexBuffer.get(), fCubicBuffer.get(), nullptr);
Chris Daltonb5391d92020-05-24 14:55:54 -0600550 flushState->drawIndexedIndirect(fIndirectDrawBuffer.get(), fIndirectDrawOffset,
551 fIndirectDrawCount);
552 } else {
553 flushState->bindBuffers(nullptr, nullptr, fCubicBuffer.get());
554 flushState->draw(fCubicVertexCount, fBaseCubicVertex);
555 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
556 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
557 }
558 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700559 }
560}
561
Chris Daltonb5391d92020-05-24 14:55:54 -0600562void GrTessellatePathOp::drawCoverPass(GrOpFlushState* flushState) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700563 // Allows non-zero stencil values to pass and write a color, and resets the stencil value back
564 // to zero; discards immediately on stencil values of zero.
565 // NOTE: It's ok to not check the clip here because the previous stencil pass only wrote to
566 // samples already inside the clip.
567 constexpr static GrUserStencilSettings kTestAndResetStencil(
568 GrUserStencilSettings::StaticInit<
569 0x0000,
570 GrUserStencilTest::kNotEqual,
571 0xffff,
572 GrUserStencilOp::kZero,
573 GrUserStencilOp::kKeep,
574 0xffff>());
575
576 GrPipeline::InitArgs initArgs;
577 if (GrAAType::kNone != fAAType) {
578 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
Chris Daltonb5391d92020-05-24 14:55:54 -0600579 if (1 == flushState->proxy()->numSamples()) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700580 SkASSERT(GrAAType::kCoverage == fAAType);
581 // We are mixed sampled. Use conservative raster to make the sample coverage mask 100%
582 // at every fragment. This way we will still get a double hit on shared edges, but
583 // whichever side comes first will cover every sample and will clear the stencil. The
584 // other side will then be discarded and not cause a double blend.
585 initArgs.fInputFlags |= GrPipeline::InputFlags::kConservativeRaster;
586 }
587 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600588 initArgs.fCaps = &flushState->caps();
589 initArgs.fDstProxyView = flushState->drawOpArgs().dstProxyView();
590 initArgs.fWriteSwizzle = flushState->drawOpArgs().writeSwizzle();
591 GrPipeline pipeline(initArgs, std::move(fProcessors), flushState->detachAppliedClip());
Chris Daltonb832ce62020-01-06 19:49:37 -0700592
Chris Dalton04f9cda2020-04-23 10:04:25 -0600593 if (fDoFillTriangleBuffer) {
594 SkASSERT(fTriangleBuffer);
Chris Daltonb832ce62020-01-06 19:49:37 -0700595
Chris Dalton04f9cda2020-04-23 10:04:25 -0600596 // These are a twist on the standard red book stencil settings that allow us to fill the
Chris Dalton4328e922020-01-29 13:16:14 -0700597 // inner polygon directly to the final render target. At this point, the curves are already
598 // stencilled in. So if the stencil value is zero, then it means the path at our sample is
599 // not affected by any curves and we fill the path in directly. If the stencil value is
600 // nonzero, then we don't fill and instead continue the standard red book stencil process.
601 //
602 // NOTE: These settings are currently incompatible with a stencil clip.
603 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
604 GrUserStencilSettings::StaticInitSeparate<
605 0x0000, 0x0000,
606 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
607 0xffff, 0xffff,
608 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
609 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
610 0xffff, 0xffff>());
611
612 constexpr static GrUserStencilSettings kFillOrInvertStencil(
613 GrUserStencilSettings::StaticInit<
614 0x0000,
615 GrUserStencilTest::kEqual,
616 0xffff,
617 GrUserStencilOp::kKeep,
618 GrUserStencilOp::kZero,
619 0xffff>());
620
Chris Dalton04f9cda2020-04-23 10:04:25 -0600621 if (fDoStencilTriangleBuffer) {
Chris Dalton4328e922020-01-29 13:16:14 -0700622 // The path was already stencilled. Here we just need to do a cover pass.
623 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600624 } else if (!fStencilCubicsShader) {
625 // There are no stencilled curves. We can ignore stencil and fill the path directly.
Chris Dalton4328e922020-01-29 13:16:14 -0700626 pipeline.setUserStencil(&GrUserStencilSettings::kUnused);
627 } else if (SkPathFillType::kWinding == fPath.getFillType()) {
628 // Fill in the path pixels not touched by curves, incr/decr stencil otherwise.
629 SkASSERT(!pipeline.hasStencilClip());
630 pipeline.setUserStencil(&kFillOrIncrDecrStencil);
631 } else {
632 // Fill in the path pixels not touched by curves, invert stencil otherwise.
633 SkASSERT(!pipeline.hasStencilClip());
634 pipeline.setUserStencil(&kFillOrInvertStencil);
635 }
Chris Dalton4328e922020-01-29 13:16:14 -0700636
Chris Dalton04f9cda2020-04-23 10:04:25 -0600637 GrFillTriangleShader fillTriangleShader(fViewMatrix, fColor);
Chris Daltonb5391d92020-05-24 14:55:54 -0600638 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
639 &fillTriangleShader);
640 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
641 flushState->bindTextures(fillTriangleShader, nullptr, pipeline);
642 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer.get());
643 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600644
645 if (fStencilCubicsShader) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600646 SkASSERT(fCubicBuffer);
647
Chris Dalton4328e922020-01-29 13:16:14 -0700648 // At this point, every pixel is filled in except the ones touched by curves. Issue a
649 // final cover pass over the curves by drawing their convex hulls. This will fill in any
650 // remaining samples and reset the stencil buffer.
Chris Dalton4328e922020-01-29 13:16:14 -0700651 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600652 GrFillCubicHullShader fillCubicHullShader(fViewMatrix, fColor);
Chris Daltonb5391d92020-05-24 14:55:54 -0600653 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
Chris Dalton04f9cda2020-04-23 10:04:25 -0600654 &fillCubicHullShader);
Chris Daltonb5391d92020-05-24 14:55:54 -0600655 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
656 flushState->bindTextures(fillCubicHullShader, nullptr, pipeline);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600657
658 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with
659 // the base vertex on an instance boundary in order to accommodate this.
660 SkASSERT((fCubicVertexCount % 4) == 0);
661 SkASSERT((fBaseCubicVertex % 4) == 0);
Chris Daltonb5391d92020-05-24 14:55:54 -0600662 flushState->bindBuffers(nullptr, fCubicBuffer.get(), nullptr);
663 flushState->drawInstanced(fCubicVertexCount >> 2, fBaseCubicVertex >> 2, 4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700664 }
Chris Dalton42915c22020-04-22 16:24:43 -0600665 return;
Chris Dalton4328e922020-01-29 13:16:14 -0700666 }
Chris Dalton42915c22020-04-22 16:24:43 -0600667
Chris Dalton04f9cda2020-04-23 10:04:25 -0600668 // There are no triangles to fill. Just draw a bounding box.
Chris Dalton42915c22020-04-22 16:24:43 -0600669 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600670 GrFillBoundingBoxShader fillBoundingBoxShader(fViewMatrix, fColor, fPath.getBounds());
Chris Daltonb5391d92020-05-24 14:55:54 -0600671 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
672 &fillBoundingBoxShader);
673 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
674 flushState->bindTextures(fillBoundingBoxShader, nullptr, pipeline);
675 flushState->bindBuffers(nullptr, nullptr, nullptr);
676 flushState->draw(4, 0);
Chris Daltonb832ce62020-01-06 19:49:37 -0700677}