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