xref: /aosp_15_r20/external/skia/src/gpu/ganesh/tessellate/PathTessellator.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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