• 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/StrokeTessellator.h"
9 
10 #include "src/core/SkGeometry.h"
11 #include "src/core/SkPathPriv.h"
12 #include "src/gpu/ganesh/GrCaps.h"
13 #include "src/gpu/ganesh/GrMeshDrawTarget.h"
14 #include "src/gpu/ganesh/GrOpFlushState.h"
15 #include "src/gpu/ganesh/GrResourceProvider.h"
16 #include "src/gpu/ganesh/tessellate/VertexChunkPatchAllocator.h"
17 #include "src/gpu/tessellate/PatchWriter.h"
18 #include "src/gpu/tessellate/StrokeIterator.h"
19 #include "src/gpu/tessellate/WangsFormula.h"
20 
21 namespace skgpu::v1 {
22 
23 namespace {
24 
25 using namespace skgpu::tess;
26 
27 using StrokeWriter = PatchWriter<VertexChunkPatchAllocator,
28                                  Required<PatchAttribs::kJoinControlPoint>,
29                                  Optional<PatchAttribs::kStrokeParams>,
30                                  Optional<PatchAttribs::kColor>,
31                                  Optional<PatchAttribs::kWideColorIfEnabled>,
32                                  Optional<PatchAttribs::kExplicitCurveType>,
33                                  ReplicateLineEndPoints,
34                                  TrackJoinControlPoints>;
35 
write_fixed_count_patches(StrokeWriter && patchWriter,const SkMatrix & shaderMatrix,StrokeTessellator::PathStrokeList * pathStrokeList)36 void write_fixed_count_patches(StrokeWriter&& patchWriter,
37                                const SkMatrix& shaderMatrix,
38                                StrokeTessellator::PathStrokeList* pathStrokeList) {
39     // The vector xform approximates how the control points are transformed by the shader to
40     // more accurately compute how many *parametric* segments are needed.
41     // getMaxScale() returns -1 if it can't compute a scale factor (e.g. perspective), taking the
42     // absolute value automatically converts that to an identity scale factor for our purposes.
43     patchWriter.setShaderTransform(wangs_formula::VectorXform{shaderMatrix},
44                                    std::abs(shaderMatrix.getMaxScale()));
45     if (!(patchWriter.attribs() & PatchAttribs::kStrokeParams)) {
46         // Strokes are static. Calculate tolerances once.
47         patchWriter.updateUniformStrokeParams(pathStrokeList->fStroke);
48     }
49 
50     for (auto* pathStroke = pathStrokeList; pathStroke; pathStroke = pathStroke->fNext) {
51         const SkStrokeRec& stroke = pathStroke->fStroke;
52         if (patchWriter.attribs() & PatchAttribs::kStrokeParams) {
53             // Strokes are dynamic. Calculate tolerances every time.
54             patchWriter.updateStrokeParamsAttrib(stroke);
55         }
56         if (patchWriter.attribs() & PatchAttribs::kColor) {
57             patchWriter.updateColorAttrib(pathStroke->fColor);
58         }
59 
60         StrokeIterator strokeIter(pathStroke->fPath, &pathStroke->fStroke, &shaderMatrix);
61         while (strokeIter.next()) {
62             using Verb = StrokeIterator::Verb;
63             const SkPoint* p = strokeIter.pts();
64             int numChops;
65             switch (strokeIter.verb()) {
66                 case Verb::kContourFinished:
67                     patchWriter.writeDeferredStrokePatch();
68                     break;
69                 case Verb::kCircle:
70                     // Round cap or else an empty stroke that is specified to be drawn as a circle.
71                     patchWriter.writeCircle(p[0]);
72                     [[fallthrough]];
73                 case Verb::kMoveWithinContour:
74                     // A regular kMove invalidates the previous control point; the stroke iterator
75                     // tells us a new value to use.
76                     patchWriter.updateJoinControlPointAttrib(p[0]);
77                     break;
78                 case Verb::kLine:
79                     patchWriter.writeLine(p[0], p[1]);
80                     break;
81                 case Verb::kQuad:
82                     if (ConicHasCusp(p)) {
83                         // The cusp is always at the midtandent.
84                         SkPoint cusp = SkEvalQuadAt(p, SkFindQuadMidTangent(p));
85                         patchWriter.writeCircle(cusp);
86                         // A quad can only have a cusp if it's flat with a 180-degree turnaround.
87                         patchWriter.writeLine(p[0], cusp);
88                         patchWriter.writeLine(cusp, p[2]);
89                     } else {
90                         patchWriter.writeQuadratic(p);
91                     }
92                     break;
93                 case Verb::kConic:
94                     if (ConicHasCusp(p)) {
95                         // The cusp is always at the midtandent.
96                         SkConic conic(p, strokeIter.w());
97                         SkPoint cusp = conic.evalAt(conic.findMidTangent());
98                         patchWriter.writeCircle(cusp);
99                         // A conic can only have a cusp if it's flat with a 180-degree turnaround.
100                         patchWriter.writeLine(p[0], cusp);
101                         patchWriter.writeLine(cusp, p[2]);
102                     } else {
103                         patchWriter.writeConic(p, strokeIter.w());
104                     }
105                     break;
106                 case Verb::kCubic:
107                     SkPoint chops[10];
108                     float T[2];
109                     bool areCusps;
110                     numChops = FindCubicConvex180Chops(p, T, &areCusps);
111                     if (numChops == 0) {
112                         patchWriter.writeCubic(p);
113                     } else if (numChops == 1) {
114                         SkChopCubicAt(p, chops, T[0]);
115                         if (areCusps) {
116                             patchWriter.writeCircle(chops[3]);
117                             // In a perfect world, these 3 points would be be equal after chopping
118                             // on a cusp.
119                             chops[2] = chops[4] = chops[3];
120                         }
121                         patchWriter.writeCubic(chops);
122                         patchWriter.writeCubic(chops + 3);
123                     } else {
124                         SkASSERT(numChops == 2);
125                         SkChopCubicAt(p, chops, T[0], T[1]);
126                         if (areCusps) {
127                             patchWriter.writeCircle(chops[3]);
128                             patchWriter.writeCircle(chops[6]);
129                             // Two cusps are only possible if it's a flat line with two 180-degree
130                             // turnarounds.
131                             patchWriter.writeLine(chops[0], chops[3]);
132                             patchWriter.writeLine(chops[3], chops[6]);
133                             patchWriter.writeLine(chops[6], chops[9]);
134                         } else {
135                             patchWriter.writeCubic(chops);
136                             patchWriter.writeCubic(chops + 3);
137                             patchWriter.writeCubic(chops + 6);
138                         }
139                     }
140                     break;
141             }
142         }
143     }
144 }
145 
146 }  // namespace
147 
148 
149 SKGPU_DECLARE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey);
150 
prepare(GrMeshDrawTarget * target,const SkMatrix & shaderMatrix,PathStrokeList * pathStrokeList,int totalCombinedStrokeVerbCnt)151 void StrokeTessellator::prepare(GrMeshDrawTarget* target,
152                                 const SkMatrix& shaderMatrix,
153                                 PathStrokeList* pathStrokeList,
154                                 int totalCombinedStrokeVerbCnt) {
155     LinearTolerances worstCase;
156     const int preallocCount = FixedCountStrokes::PreallocCount(totalCombinedStrokeVerbCnt);
157     StrokeWriter patchWriter{fAttribs, &worstCase,  target, &fVertexChunkArray, preallocCount};
158 
159     write_fixed_count_patches(std::move(patchWriter), shaderMatrix, pathStrokeList);
160     fVertexCount = FixedCountStrokes::VertexCount(worstCase);
161 
162     if (!target->caps().shaderCaps()->fVertexIDSupport) {
163         // Our shader won't be able to use sk_VertexID. Bind a fallback vertex buffer with the IDs
164         // in it instead.
165         fVertexCount = std::min(fVertexCount, 2 * FixedCountStrokes::kMaxEdgesNoVertexIDs);
166 
167         SKGPU_DEFINE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey);
168 
169         fVertexBufferIfNoIDSupport = target->resourceProvider()->findOrMakeStaticBuffer(
170                 GrGpuBufferType::kVertex,
171                 FixedCountStrokes::VertexBufferSize(),
172                 gVertexIDFallbackBufferKey,
173                 FixedCountStrokes::WriteVertexBuffer);
174     }
175 }
176 
draw(GrOpFlushState * flushState) const177 void StrokeTessellator::draw(GrOpFlushState* flushState) const {
178     if (fVertexChunkArray.empty() || fVertexCount <= 0) {
179         return;
180     }
181     if (!flushState->caps().shaderCaps()->fVertexIDSupport &&
182         !fVertexBufferIfNoIDSupport) {
183         return;
184     }
185     for (const auto& instanceChunk : fVertexChunkArray) {
186         flushState->bindBuffers(nullptr, instanceChunk.fBuffer, fVertexBufferIfNoIDSupport);
187         flushState->drawInstanced(instanceChunk.fCount,
188                                   instanceChunk.fBase,
189                                   fVertexCount,
190                                   0);
191     }
192 }
193 
194 }  // namespace skgpu::v1
195