blob: 6477f2519d9da7fd78bb803d311312a282d0aba7 [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) {
Chris Daltona6858ae2020-06-25 07:04:33 -0600328 int instanceCountAtCurrLevel = resolveLevelCounter[resolveLevel];
329 if (!instanceCountAtCurrLevel) {
330 SkDEBUGCODE(instanceLocations[resolveLevel] = nullptr;)
331 continue;
Chris Daltonb5391d92020-05-24 14:55:54 -0600332 }
Chris Daltona6858ae2020-06-25 07:04:33 -0600333 instanceLocations[resolveLevel] = cubicData + runningInstanceCount * 4;
334 indirectData[indirectIdx++] = GrMiddleOutCubicShader::MakeDrawCubicsIndirectCmd(
335 resolveLevel, instanceCountAtCurrLevel, baseInstance + runningInstanceCount);
336 runningInstanceCount += instanceCountAtCurrLevel;
Chris Daltonb5391d92020-05-24 14:55:54 -0600337 }
338
339#ifdef SK_DEBUG
340 SkASSERT(indirectIdx == fIndirectDrawCount);
341 SkASSERT(runningInstanceCount == numTrianglesAtBeginningOfData +
342 resolveLevelCounter.totalCubicInstanceCount());
343 SkASSERT(fIndirectDrawCount > 0);
344
345 SkPoint* endLocations[kMaxResolveLevel + 1];
Chris Daltona6858ae2020-06-25 07:04:33 -0600346 int lastResolveLevel = 0;
347 for (int resolveLevel = 1; resolveLevel <= kMaxResolveLevel; ++resolveLevel) {
348 if (!instanceLocations[resolveLevel]) {
349 endLocations[resolveLevel] = nullptr;
350 continue;
351 }
352 endLocations[lastResolveLevel] = instanceLocations[resolveLevel];
353 lastResolveLevel = resolveLevel;
354 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600355 int totalInstanceCount = numTrianglesAtBeginningOfData +
356 resolveLevelCounter.totalCubicInstanceCount();
Chris Daltona6858ae2020-06-25 07:04:33 -0600357 endLocations[lastResolveLevel] = cubicData + totalInstanceCount * 4;
Chris Daltonb5391d92020-05-24 14:55:54 -0600358#endif
359
360 fCubicVertexCount = numTrianglesAtBeginningOfData * 4;
361
362 if (resolveLevelCounter.totalCubicInstanceCount()) {
363 GrVectorXform xform(fViewMatrix);
364 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
365 int level;
366 switch (verb) {
367 default:
368 continue;
369 case SkPathVerb::kQuad:
Chris Daltonb96995d2020-06-04 16:44:29 -0600370 level = GrWangsFormula::quadratic_log2(kLinearizationIntolerance, pts, xform);
Chris Daltonb5391d92020-05-24 14:55:54 -0600371 if (level == 0) {
372 continue;
373 }
374 level = std::min(level, kMaxResolveLevel);
375 quad2cubic(pts, instanceLocations[level]);
376 break;
377 case SkPathVerb::kCubic:
Chris Daltonb96995d2020-06-04 16:44:29 -0600378 level = GrWangsFormula::cubic_log2(kLinearizationIntolerance, pts, xform);
Chris Daltonb5391d92020-05-24 14:55:54 -0600379 if (level == 0) {
380 continue;
381 }
382 level = std::min(level, kMaxResolveLevel);
383 memcpy(instanceLocations[level], pts, sizeof(SkPoint) * 4);
384 break;
385 }
386 instanceLocations[level] += 4;
387 fCubicVertexCount += 4;
388 }
389 }
390
391#ifdef SK_DEBUG
392 for (int i = 1; i <= kMaxResolveLevel; ++i) {
393 SkASSERT(instanceLocations[i] == endLocations[i]);
394 }
395 SkASSERT(fCubicVertexCount == (numTrianglesAtBeginningOfData +
396 resolveLevelCounter.totalCubicInstanceCount()) * 4);
397#endif
398
399 fStencilCubicsShader = target->allocator()->make<GrMiddleOutCubicShader>(fViewMatrix);
400}
401
402void GrTessellatePathOp::prepareTessellatedOuterCubics(GrMeshDrawOp::Target* target,
403 int numCountedCurves) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600404 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
Chris Daltonb5391d92020-05-24 14:55:54 -0600405 SkASSERT(numCountedCurves >= 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600406 SkASSERT(!fCubicBuffer);
407 SkASSERT(!fStencilCubicsShader);
Chris Dalton42915c22020-04-22 16:24:43 -0600408
409 if (numCountedCurves == 0) {
410 return;
411 }
412
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600413 auto* vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
Chris Daltonb5391d92020-05-24 14:55:54 -0600414 sizeof(SkPoint), numCountedCurves * 4, &fCubicBuffer, &fBaseCubicVertex));
Chris Dalton04f9cda2020-04-23 10:04:25 -0600415 if (!vertexData) {
Chris Dalton42915c22020-04-22 16:24:43 -0600416 return;
417 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600418 fCubicVertexCount = 0;
Chris Dalton42915c22020-04-22 16:24:43 -0600419
Chris Daltonf7a33072020-05-01 10:33:08 -0600420 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
421 switch (verb) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600422 default:
423 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600424 case SkPathVerb::kQuad:
425 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
426 quad2cubic(pts, vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600427 break;
428 case SkPathVerb::kCubic:
429 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
430 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
Chris Daltonf7a33072020-05-01 10:33:08 -0600431 break;
Chris Dalton42915c22020-04-22 16:24:43 -0600432 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600433 fCubicVertexCount += 4;
Chris Dalton42915c22020-04-22 16:24:43 -0600434 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600435 SkASSERT(fCubicVertexCount == numCountedCurves * 4);
436
Chris Daltonb5391d92020-05-24 14:55:54 -0600437 fStencilCubicsShader = target->allocator()->make<GrTessellateCubicShader>(fViewMatrix);
Chris Dalton42915c22020-04-22 16:24:43 -0600438}
439
Chris Daltonb5391d92020-05-24 14:55:54 -0600440void GrTessellatePathOp::prepareTessellatedCubicWedges(GrMeshDrawOp::Target* target) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600441 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
Chris Dalton04f9cda2020-04-23 10:04:25 -0600442 SkASSERT(!fCubicBuffer);
443 SkASSERT(!fStencilCubicsShader);
444
Chris Dalton42915c22020-04-22 16:24:43 -0600445 // No initial moveTo, one wedge per verb, plus an implicit close at the end.
446 // Each wedge has 5 vertices.
447 int maxVertices = (fPath.countVerbs() + 1) * 5;
448
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600449 GrEagerDynamicVertexAllocator vertexAlloc(target, &fCubicBuffer, &fBaseCubicVertex);
Chris Dalton42915c22020-04-22 16:24:43 -0600450 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
451 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600452 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600453 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600454 fCubicVertexCount = 0;
Chris Dalton42915c22020-04-22 16:24:43 -0600455
456 GrMidpointContourParser parser(fPath);
457 while (parser.parseNextContour()) {
Chris Daltonf7a33072020-05-01 10:33:08 -0600458 SkPoint midpoint = parser.currentMidpoint();
459 SkPoint startPoint = {0, 0};
460 SkPoint lastPoint = startPoint;
461 for (auto [verb, pts, w] : parser.currentContour()) {
462 switch (verb) {
463 case SkPathVerb::kMove:
464 startPoint = lastPoint = pts[0];
Chris Dalton42915c22020-04-22 16:24:43 -0600465 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600466 case SkPathVerb::kClose:
467 continue; // Ignore. We can assume an implicit close at the end.
Chris Dalton42915c22020-04-22 16:24:43 -0600468 case SkPathVerb::kLine:
Chris Daltonf7a33072020-05-01 10:33:08 -0600469 line2cubic(pts[0], pts[1], vertexData + fCubicVertexCount);
470 lastPoint = pts[1];
Chris Dalton42915c22020-04-22 16:24:43 -0600471 break;
472 case SkPathVerb::kQuad:
Chris Daltonf7a33072020-05-01 10:33:08 -0600473 quad2cubic(pts, vertexData + fCubicVertexCount);
474 lastPoint = pts[2];
Chris Dalton42915c22020-04-22 16:24:43 -0600475 break;
476 case SkPathVerb::kCubic:
Chris Daltonf7a33072020-05-01 10:33:08 -0600477 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
478 lastPoint = pts[3];
Chris Dalton42915c22020-04-22 16:24:43 -0600479 break;
480 case SkPathVerb::kConic:
481 SkUNREACHABLE;
482 }
Chris Daltonf7a33072020-05-01 10:33:08 -0600483 vertexData[fCubicVertexCount + 4] = midpoint;
484 fCubicVertexCount += 5;
485 }
486 if (lastPoint != startPoint) {
487 line2cubic(lastPoint, startPoint, vertexData + fCubicVertexCount);
488 vertexData[fCubicVertexCount + 4] = midpoint;
Chris Dalton04f9cda2020-04-23 10:04:25 -0600489 fCubicVertexCount += 5;
Chris Dalton42915c22020-04-22 16:24:43 -0600490 }
491 }
492
Chris Dalton04f9cda2020-04-23 10:04:25 -0600493 vertexAlloc.unlock(fCubicVertexCount);
494
495 if (fCubicVertexCount) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600496 fStencilCubicsShader = target->allocator()->make<GrTessellateWedgeShader>(fViewMatrix);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600497 }
Chris Dalton42915c22020-04-22 16:24:43 -0600498}
499
Chris Daltonb5391d92020-05-24 14:55:54 -0600500void GrTessellatePathOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
501 this->drawStencilPass(flushState);
Chris Daltonb96995d2020-06-04 16:44:29 -0600502 if (!(OpFlags::kStencilOnly & fOpFlags)) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600503 this->drawCoverPass(flushState);
Chris Daltonb832ce62020-01-06 19:49:37 -0700504 }
505}
506
Chris Daltonb5391d92020-05-24 14:55:54 -0600507void GrTessellatePathOp::drawStencilPass(GrOpFlushState* flushState) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700508 // Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
509 constexpr static GrUserStencilSettings kIncrDecrStencil(
510 GrUserStencilSettings::StaticInitSeparate<
511 0x0000, 0x0000,
512 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
513 0xffff, 0xffff,
514 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
515 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
516 0xffff, 0xffff>());
517
518 // Inverts the bottom stencil bit. Used for "even/odd" fill.
519 constexpr static GrUserStencilSettings kInvertStencil(
520 GrUserStencilSettings::StaticInit<
521 0x0000,
522 GrUserStencilTest::kAlwaysIfInClip,
523 0xffff,
524 GrUserStencilOp::kInvert,
525 GrUserStencilOp::kKeep,
526 0x0001>());
527
528 GrPipeline::InitArgs initArgs;
529 if (GrAAType::kNone != fAAType) {
530 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
531 }
Chris Daltonb96995d2020-06-04 16:44:29 -0600532 if (flushState->caps().wireframeSupport() && (OpFlags::kWireframe & fOpFlags)) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700533 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
534 }
535 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
536 SkPathFillType::kEvenOdd == fPath.getFillType());
537 initArgs.fUserStencil = (SkPathFillType::kWinding == fPath.getFillType()) ?
538 &kIncrDecrStencil : &kInvertStencil;
Chris Daltonb5391d92020-05-24 14:55:54 -0600539 initArgs.fCaps = &flushState->caps();
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600540 GrPipeline pipeline(initArgs, GrDisableColorXPFactory::MakeXferProcessor(),
Chris Daltonb5391d92020-05-24 14:55:54 -0600541 flushState->appliedHardClip());
Chris Dalton012f8492020-03-05 11:49:15 -0700542
Chris Dalton04f9cda2020-04-23 10:04:25 -0600543 if (fDoStencilTriangleBuffer) {
544 SkASSERT(fTriangleBuffer);
545 GrStencilTriangleShader stencilTriangleShader(fViewMatrix);
Chris Daltonb5391d92020-05-24 14:55:54 -0600546 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
Chris Dalton04f9cda2020-04-23 10:04:25 -0600547 &stencilTriangleShader);
Chris Daltonb5391d92020-05-24 14:55:54 -0600548 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
549 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer.get());
550 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700551 }
552
Chris Dalton04f9cda2020-04-23 10:04:25 -0600553 if (fStencilCubicsShader) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600554 SkASSERT(fCubicBuffer);
555 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
556 fStencilCubicsShader);
557 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
558 if (fIndirectDrawBuffer) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600559 SkASSERT(fIndirectIndexBuffer);
560 flushState->bindBuffers(fIndirectIndexBuffer.get(), fCubicBuffer.get(), nullptr);
Chris Daltonb5391d92020-05-24 14:55:54 -0600561 flushState->drawIndexedIndirect(fIndirectDrawBuffer.get(), fIndirectDrawOffset,
562 fIndirectDrawCount);
563 } else {
564 flushState->bindBuffers(nullptr, nullptr, fCubicBuffer.get());
565 flushState->draw(fCubicVertexCount, fBaseCubicVertex);
566 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
567 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
568 }
569 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700570 }
571}
572
Chris Daltonb5391d92020-05-24 14:55:54 -0600573void GrTessellatePathOp::drawCoverPass(GrOpFlushState* flushState) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700574 // Allows non-zero stencil values to pass and write a color, and resets the stencil value back
575 // to zero; discards immediately on stencil values of zero.
576 // NOTE: It's ok to not check the clip here because the previous stencil pass only wrote to
577 // samples already inside the clip.
578 constexpr static GrUserStencilSettings kTestAndResetStencil(
579 GrUserStencilSettings::StaticInit<
580 0x0000,
581 GrUserStencilTest::kNotEqual,
582 0xffff,
583 GrUserStencilOp::kZero,
584 GrUserStencilOp::kKeep,
585 0xffff>());
586
587 GrPipeline::InitArgs initArgs;
588 if (GrAAType::kNone != fAAType) {
589 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
Chris Daltonb5391d92020-05-24 14:55:54 -0600590 if (1 == flushState->proxy()->numSamples()) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700591 SkASSERT(GrAAType::kCoverage == fAAType);
592 // We are mixed sampled. Use conservative raster to make the sample coverage mask 100%
593 // at every fragment. This way we will still get a double hit on shared edges, but
594 // whichever side comes first will cover every sample and will clear the stencil. The
595 // other side will then be discarded and not cause a double blend.
596 initArgs.fInputFlags |= GrPipeline::InputFlags::kConservativeRaster;
597 }
598 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600599 initArgs.fCaps = &flushState->caps();
600 initArgs.fDstProxyView = flushState->drawOpArgs().dstProxyView();
601 initArgs.fWriteSwizzle = flushState->drawOpArgs().writeSwizzle();
602 GrPipeline pipeline(initArgs, std::move(fProcessors), flushState->detachAppliedClip());
Chris Daltonb832ce62020-01-06 19:49:37 -0700603
Chris Dalton04f9cda2020-04-23 10:04:25 -0600604 if (fDoFillTriangleBuffer) {
605 SkASSERT(fTriangleBuffer);
Chris Daltonb832ce62020-01-06 19:49:37 -0700606
Chris Dalton04f9cda2020-04-23 10:04:25 -0600607 // These are a twist on the standard red book stencil settings that allow us to fill the
Chris Dalton4328e922020-01-29 13:16:14 -0700608 // inner polygon directly to the final render target. At this point, the curves are already
609 // stencilled in. So if the stencil value is zero, then it means the path at our sample is
610 // not affected by any curves and we fill the path in directly. If the stencil value is
611 // nonzero, then we don't fill and instead continue the standard red book stencil process.
612 //
613 // NOTE: These settings are currently incompatible with a stencil clip.
614 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
615 GrUserStencilSettings::StaticInitSeparate<
616 0x0000, 0x0000,
617 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
618 0xffff, 0xffff,
619 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
620 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
621 0xffff, 0xffff>());
622
623 constexpr static GrUserStencilSettings kFillOrInvertStencil(
624 GrUserStencilSettings::StaticInit<
625 0x0000,
626 GrUserStencilTest::kEqual,
627 0xffff,
628 GrUserStencilOp::kKeep,
629 GrUserStencilOp::kZero,
630 0xffff>());
631
Chris Dalton04f9cda2020-04-23 10:04:25 -0600632 if (fDoStencilTriangleBuffer) {
Chris Dalton4328e922020-01-29 13:16:14 -0700633 // The path was already stencilled. Here we just need to do a cover pass.
634 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600635 } else if (!fStencilCubicsShader) {
636 // There are no stencilled curves. We can ignore stencil and fill the path directly.
Chris Dalton4328e922020-01-29 13:16:14 -0700637 pipeline.setUserStencil(&GrUserStencilSettings::kUnused);
638 } else if (SkPathFillType::kWinding == fPath.getFillType()) {
639 // Fill in the path pixels not touched by curves, incr/decr stencil otherwise.
640 SkASSERT(!pipeline.hasStencilClip());
641 pipeline.setUserStencil(&kFillOrIncrDecrStencil);
642 } else {
643 // Fill in the path pixels not touched by curves, invert stencil otherwise.
644 SkASSERT(!pipeline.hasStencilClip());
645 pipeline.setUserStencil(&kFillOrInvertStencil);
646 }
Chris Dalton4328e922020-01-29 13:16:14 -0700647
Chris Dalton04f9cda2020-04-23 10:04:25 -0600648 GrFillTriangleShader fillTriangleShader(fViewMatrix, fColor);
Chris Daltonb5391d92020-05-24 14:55:54 -0600649 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
650 &fillTriangleShader);
651 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
652 flushState->bindTextures(fillTriangleShader, nullptr, pipeline);
653 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer.get());
654 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600655
656 if (fStencilCubicsShader) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600657 SkASSERT(fCubicBuffer);
658
Chris Dalton4328e922020-01-29 13:16:14 -0700659 // At this point, every pixel is filled in except the ones touched by curves. Issue a
660 // final cover pass over the curves by drawing their convex hulls. This will fill in any
661 // remaining samples and reset the stencil buffer.
Chris Dalton4328e922020-01-29 13:16:14 -0700662 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600663 GrFillCubicHullShader fillCubicHullShader(fViewMatrix, fColor);
Chris Daltonb5391d92020-05-24 14:55:54 -0600664 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
Chris Dalton04f9cda2020-04-23 10:04:25 -0600665 &fillCubicHullShader);
Chris Daltonb5391d92020-05-24 14:55:54 -0600666 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
667 flushState->bindTextures(fillCubicHullShader, nullptr, pipeline);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600668
669 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with
670 // the base vertex on an instance boundary in order to accommodate this.
671 SkASSERT((fCubicVertexCount % 4) == 0);
672 SkASSERT((fBaseCubicVertex % 4) == 0);
Chris Daltonb5391d92020-05-24 14:55:54 -0600673 flushState->bindBuffers(nullptr, fCubicBuffer.get(), nullptr);
674 flushState->drawInstanced(fCubicVertexCount >> 2, fBaseCubicVertex >> 2, 4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700675 }
Chris Dalton42915c22020-04-22 16:24:43 -0600676 return;
Chris Dalton4328e922020-01-29 13:16:14 -0700677 }
Chris Dalton42915c22020-04-22 16:24:43 -0600678
Chris Dalton04f9cda2020-04-23 10:04:25 -0600679 // There are no triangles to fill. Just draw a bounding box.
Chris Dalton42915c22020-04-22 16:24:43 -0600680 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600681 GrFillBoundingBoxShader fillBoundingBoxShader(fViewMatrix, fColor, fPath.getBounds());
Chris Daltonb5391d92020-05-24 14:55:54 -0600682 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
683 &fillBoundingBoxShader);
684 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
685 flushState->bindTextures(fillBoundingBoxShader, nullptr, pipeline);
686 flushState->bindBuffers(nullptr, nullptr, nullptr);
687 flushState->draw(4, 0);
Chris Daltonb832ce62020-01-06 19:49:37 -0700688}