1 /*
2 * Copyright 2022 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/ganesh/tessellate/PathTessellator.h"
9
10 #include "src/core/SkPathPriv.h"
11 #include "src/gpu/ganesh/GrMeshDrawTarget.h"
12 #include "src/gpu/ganesh/GrOpFlushState.h"
13 #include "src/gpu/ganesh/GrResourceProvider.h"
14 #include "src/gpu/ganesh/tessellate/VertexChunkPatchAllocator.h"
15 #include "src/gpu/tessellate/AffineMatrix.h"
16 #include "src/gpu/tessellate/FixedCountBufferUtils.h"
17 #include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
18 #include "src/gpu/tessellate/MidpointContourParser.h"
19 #include "src/gpu/tessellate/PatchWriter.h"
20 #include "src/gpu/tessellate/WangsFormula.h"
21
22 namespace skgpu::v1 {
23
24 namespace {
25
26 using namespace skgpu::tess;
27
28 using CurveWriter = PatchWriter<VertexChunkPatchAllocator,
29 Optional<PatchAttribs::kColor>,
30 Optional<PatchAttribs::kWideColorIfEnabled>,
31 Optional<PatchAttribs::kExplicitCurveType>,
32 AddTrianglesWhenChopping,
33 DiscardFlatCurves>;
34
write_curve_patches(CurveWriter && patchWriter,const SkMatrix & shaderMatrix,const PathTessellator::PathDrawList & pathDrawList)35 void write_curve_patches(CurveWriter&& patchWriter,
36 const SkMatrix& shaderMatrix,
37 const PathTessellator::PathDrawList& pathDrawList) {
38 patchWriter.setShaderTransform(wangs_formula::VectorXform{shaderMatrix});
39 for (auto [pathMatrix, path, color] : pathDrawList) {
40 AffineMatrix m(pathMatrix);
41 if (patchWriter.attribs() & PatchAttribs::kColor) {
42 patchWriter.updateColorAttrib(color);
43 }
44 for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
45 switch (verb) {
46 case SkPathVerb::kQuad: {
47 auto [p0, p1] = m.map2Points(pts);
48 auto p2 = m.map1Point(pts+2);
49
50 patchWriter.writeQuadratic(p0, p1, p2);
51 break;
52 }
53
54 case SkPathVerb::kConic: {
55 auto [p0, p1] = m.map2Points(pts);
56 auto p2 = m.map1Point(pts+2);
57
58 patchWriter.writeConic(p0, p1, p2, *w);
59 break;
60 }
61
62 case SkPathVerb::kCubic: {
63 auto [p0, p1] = m.map2Points(pts);
64 auto [p2, p3] = m.map2Points(pts+2);
65
66 patchWriter.writeCubic(p0, p1, p2, p3);
67 break;
68 }
69
70 default: break;
71 }
72 }
73 }
74 }
75
76 using WedgeWriter = PatchWriter<VertexChunkPatchAllocator,
77 Required<PatchAttribs::kFanPoint>,
78 Optional<PatchAttribs::kColor>,
79 Optional<PatchAttribs::kWideColorIfEnabled>,
80 Optional<PatchAttribs::kExplicitCurveType>>;
81
write_wedge_patches(WedgeWriter && patchWriter,const SkMatrix & shaderMatrix,const PathTessellator::PathDrawList & pathDrawList)82 void write_wedge_patches(WedgeWriter&& patchWriter,
83 const SkMatrix& shaderMatrix,
84 const PathTessellator::PathDrawList& pathDrawList) {
85 patchWriter.setShaderTransform(wangs_formula::VectorXform{shaderMatrix});
86 for (auto [pathMatrix, path, color] : pathDrawList) {
87 AffineMatrix m(pathMatrix);
88 if (patchWriter.attribs() & PatchAttribs::kColor) {
89 patchWriter.updateColorAttrib(color);
90 }
91 MidpointContourParser parser(path);
92 while (parser.parseNextContour()) {
93 patchWriter.updateFanPointAttrib(m.mapPoint(parser.currentMidpoint()));
94 SkPoint lastPoint = {0, 0};
95 SkPoint startPoint = {0, 0};
96 for (auto [verb, pts, w] : parser.currentContour()) {
97 switch (verb) {
98 case SkPathVerb::kMove: {
99 startPoint = lastPoint = pts[0];
100 break;
101 }
102
103 case SkPathVerb::kLine: {
104 // Explicitly convert the line to an equivalent cubic w/ four distinct
105 // control points because it fans better and avoids double-hitting pixels.
106 patchWriter.writeLine(m.map2Points(pts));
107 lastPoint = pts[1];
108 break;
109 }
110
111 case SkPathVerb::kQuad: {
112 auto [p0, p1] = m.map2Points(pts);
113 auto p2 = m.map1Point(pts+2);
114
115 patchWriter.writeQuadratic(p0, p1, p2);
116 lastPoint = pts[2];
117 break;
118 }
119
120 case SkPathVerb::kConic: {
121 auto [p0, p1] = m.map2Points(pts);
122 auto p2 = m.map1Point(pts+2);
123
124 patchWriter.writeConic(p0, p1, p2, *w);
125 lastPoint = pts[2];
126 break;
127 }
128
129 case SkPathVerb::kCubic: {
130 auto [p0, p1] = m.map2Points(pts);
131 auto [p2, p3] = m.map2Points(pts+2);
132
133 patchWriter.writeCubic(p0, p1, p2, p3);
134 lastPoint = pts[3];
135 break;
136 }
137
138 case SkPathVerb::kClose: {
139 break; // Ignore. We can assume an implicit close at the end.
140 }
141 }
142 }
143 if (lastPoint != startPoint) {
144 SkPoint pts[2] = {lastPoint, startPoint};
145 patchWriter.writeLine(m.map2Points(pts));
146 }
147 }
148 }
149 }
150
151 } // namespace
152
153 SKGPU_DECLARE_STATIC_UNIQUE_KEY(gFixedCountCurveVertexBufferKey);
154 SKGPU_DECLARE_STATIC_UNIQUE_KEY(gFixedCountCurveIndexBufferKey);
155
prepareWithTriangles(GrMeshDrawTarget * target,const SkMatrix & shaderMatrix,GrInnerFanTriangulator::BreadcrumbTriangleList * extraTriangles,const PathDrawList & pathDrawList,int totalCombinedPathVerbCnt)156 void PathCurveTessellator::prepareWithTriangles(
157 GrMeshDrawTarget* target,
158 const SkMatrix& shaderMatrix,
159 GrInnerFanTriangulator::BreadcrumbTriangleList* extraTriangles,
160 const PathDrawList& pathDrawList,
161 int totalCombinedPathVerbCnt) {
162 #if !defined(SK_ENABLE_OPTIMIZE_SIZE)
163 int patchPreallocCount = FixedCountCurves::PreallocCount(totalCombinedPathVerbCnt) +
164 (extraTriangles ? extraTriangles->count() : 0);
165 #else
166 SkASSERT(!extraTriangles);
167 int patchPreallocCount = FixedCountCurves::PreallocCount(totalCombinedPathVerbCnt);
168 #endif
169
170
171 if (patchPreallocCount) {
172 LinearTolerances worstCase;
173 CurveWriter writer{fAttribs, &worstCase, target, &fVertexChunkArray, patchPreallocCount};
174
175 #if !defined(SK_ENABLE_OPTIMIZE_SIZE)
176 // Write out extra space-filling triangles to connect the curve patches with any external
177 // source of geometry (e.g. inner triangulation that handles winding explicitly).
178 if (extraTriangles) {
179 SkDEBUGCODE(int breadcrumbCount = 0;)
180 for (const auto* tri = extraTriangles->head(); tri; tri = tri->fNext) {
181 SkDEBUGCODE(++breadcrumbCount;)
182 auto p0 = skvx::float2::Load(tri->fPts);
183 auto p1 = skvx::float2::Load(tri->fPts + 1);
184 auto p2 = skvx::float2::Load(tri->fPts + 2);
185 if (any((p0 == p1) & (p1 == p2))) {
186 // Cull completely horizontal or vertical triangles. GrTriangulator can't always
187 // get these breadcrumb edges right when they run parallel to the sweep
188 // direction because their winding is undefined by its current definition.
189 // FIXME(skia:12060): This seemed safe, but if there is a view matrix it will
190 // introduce T-junctions.
191 continue;
192 }
193 writer.writeTriangle(p0, p1, p2);
194 }
195 SkASSERT(breadcrumbCount == extraTriangles->count());
196 }
197 #endif
198
199 write_curve_patches(std::move(writer), shaderMatrix, pathDrawList);
200 fMaxVertexCount = FixedCountCurves::VertexCount(worstCase);
201 }
202
203 GrResourceProvider* rp = target->resourceProvider();
204
205 SKGPU_DEFINE_STATIC_UNIQUE_KEY(gFixedCountCurveVertexBufferKey);
206
207 fFixedVertexBuffer = rp->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,
208 FixedCountCurves::VertexBufferSize(),
209 gFixedCountCurveVertexBufferKey,
210 FixedCountCurves::WriteVertexBuffer);
211
212 SKGPU_DEFINE_STATIC_UNIQUE_KEY(gFixedCountCurveIndexBufferKey);
213
214 fFixedIndexBuffer = rp->findOrMakeStaticBuffer(GrGpuBufferType::kIndex,
215 FixedCountCurves::IndexBufferSize(),
216 gFixedCountCurveIndexBufferKey,
217 FixedCountCurves::WriteIndexBuffer);
218 }
219
draw(GrOpFlushState * flushState) const220 void PathCurveTessellator::draw(GrOpFlushState* flushState) const {
221 if (!fFixedVertexBuffer || !fFixedIndexBuffer) {
222 return;
223 }
224 for (const GrVertexChunk& chunk : fVertexChunkArray) {
225 flushState->bindBuffers(fFixedIndexBuffer, chunk.fBuffer, fFixedVertexBuffer);
226 // The max vertex count is the logical number of vertices that the GPU needs to emit, so
227 // since we're using drawIndexedInstanced, it's provided as the "index count" parameter.
228 flushState->drawIndexedInstanced(fMaxVertexCount, 0, chunk.fCount, chunk.fBase, 0);
229 }
230 }
231
drawHullInstances(GrOpFlushState * flushState,sk_sp<const GrGpuBuffer> vertexBufferIfNeeded) const232 void PathCurveTessellator::drawHullInstances(GrOpFlushState* flushState,
233 sk_sp<const GrGpuBuffer> vertexBufferIfNeeded) const {
234 for (const GrVertexChunk& chunk : fVertexChunkArray) {
235 flushState->bindBuffers(nullptr, chunk.fBuffer, vertexBufferIfNeeded);
236 flushState->drawInstanced(chunk.fCount, chunk.fBase, 4, 0);
237 }
238 }
239
240
241 SKGPU_DECLARE_STATIC_UNIQUE_KEY(gFixedCountWedgesVertexBufferKey);
242 SKGPU_DECLARE_STATIC_UNIQUE_KEY(gFixedCountWedgesIndexBufferKey);
243
prepare(GrMeshDrawTarget * target,const SkMatrix & shaderMatrix,const PathDrawList & pathDrawList,int totalCombinedPathVerbCnt)244 void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
245 const SkMatrix& shaderMatrix,
246 const PathDrawList& pathDrawList,
247 int totalCombinedPathVerbCnt) {
248 if (int patchPreallocCount = FixedCountWedges::PreallocCount(totalCombinedPathVerbCnt)) {
249 LinearTolerances worstCase;
250 WedgeWriter writer{fAttribs, &worstCase, target, &fVertexChunkArray, patchPreallocCount};
251 write_wedge_patches(std::move(writer), shaderMatrix, pathDrawList);
252 fMaxVertexCount = FixedCountWedges::VertexCount(worstCase);
253 }
254
255 GrResourceProvider* rp = target->resourceProvider();
256
257 SKGPU_DEFINE_STATIC_UNIQUE_KEY(gFixedCountWedgesVertexBufferKey);
258
259 fFixedVertexBuffer = rp->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,
260 FixedCountWedges::VertexBufferSize(),
261 gFixedCountWedgesVertexBufferKey,
262 FixedCountWedges::WriteVertexBuffer);
263
264 SKGPU_DEFINE_STATIC_UNIQUE_KEY(gFixedCountWedgesIndexBufferKey);
265
266 fFixedIndexBuffer = rp->findOrMakeStaticBuffer(GrGpuBufferType::kIndex,
267 FixedCountWedges::IndexBufferSize(),
268 gFixedCountWedgesIndexBufferKey,
269 FixedCountWedges::WriteIndexBuffer);
270 }
271
draw(GrOpFlushState * flushState) const272 void PathWedgeTessellator::draw(GrOpFlushState* flushState) const {
273 if (!fFixedVertexBuffer || !fFixedIndexBuffer) {
274 return;
275 }
276 for (const GrVertexChunk& chunk : fVertexChunkArray) {
277 flushState->bindBuffers(fFixedIndexBuffer, chunk.fBuffer, fFixedVertexBuffer);
278 flushState->drawIndexedInstanced(fMaxVertexCount, 0, chunk.fCount, chunk.fBase, 0);
279 }
280 }
281
282 } // namespace skgpu::v1
283