• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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