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