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