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/graphite/render/TessellateStrokesRenderStep.h"
9
10 #include "include/core/SkM44.h"
11 #include "include/core/SkMatrix.h"
12 #include "include/core/SkPath.h"
13 #include "include/core/SkStrokeRec.h"
14 #include "include/private/base/SkAssert.h"
15 #include "include/private/base/SkDebug.h"
16 #include "include/private/base/SkPoint_impl.h"
17 #include "include/private/base/SkSpan_impl.h"
18 #include "src/base/SkVx.h"
19 #include "src/core/SkGeometry.h"
20 #include "src/core/SkSLTypeShared.h"
21 #include "src/gpu/graphite/Attribute.h"
22 #include "src/gpu/graphite/DrawOrder.h"
23 #include "src/gpu/graphite/DrawParams.h"
24 #include "src/gpu/graphite/DrawTypes.h"
25 #include "src/gpu/graphite/PipelineData.h"
26 #include "src/gpu/graphite/ResourceTypes.h"
27 #include "src/gpu/graphite/geom/Geometry.h"
28 #include "src/gpu/graphite/geom/Shape.h"
29 #include "src/gpu/graphite/geom/Transform.h"
30 #include "src/gpu/graphite/render/CommonDepthStencilSettings.h"
31 #include "src/gpu/graphite/render/DynamicInstancesPatchAllocator.h"
32 #include "src/gpu/tessellate/FixedCountBufferUtils.h"
33 #include "src/gpu/tessellate/PatchWriter.h"
34 #include "src/gpu/tessellate/StrokeIterator.h"
35 #include "src/gpu/tessellate/Tessellation.h"
36 #include "src/gpu/tessellate/WangsFormula.h"
37 #include "src/sksl/SkSLString.h"
38
39 namespace skgpu::graphite {
40
41 namespace {
42
43 using namespace skgpu::tess;
44
45 // Always use dynamic stroke params and join control points, track the join control point in
46 // PatchWriter and replicate line end points (match Ganesh's shader behavior).
47 //
48 // No explicit curve type on platforms that support infinity.
49 // No color or wide color attribs, since it might always be part of the PaintParams
50 // or we'll add a color-only fast path to RenderStep later.
51 static constexpr PatchAttribs kAttribs = PatchAttribs::kJoinControlPoint |
52 PatchAttribs::kStrokeParams |
53 PatchAttribs::kPaintDepth |
54 PatchAttribs::kSsboIndex;
55 static constexpr PatchAttribs kAttribsWithCurveType = kAttribs | PatchAttribs::kExplicitCurveType;
56 using Writer = PatchWriter<DynamicInstancesPatchAllocator<FixedCountStrokes>,
57 Required<PatchAttribs::kJoinControlPoint>,
58 Required<PatchAttribs::kStrokeParams>,
59 Required<PatchAttribs::kPaintDepth>,
60 Required<PatchAttribs::kSsboIndex>,
61 Optional<PatchAttribs::kExplicitCurveType>,
62 ReplicateLineEndPoints,
63 TrackJoinControlPoints>;
64
65 // The order of the attribute declarations must match the order used by
66 // PatchWriter::emitPatchAttribs, i.e.:
67 // join << fanPoint << stroke << color << depth << curveType << ssboIndices
68 static constexpr Attribute kBaseAttributes[] = {
69 {"p01", VertexAttribType::kFloat4, SkSLType::kFloat4},
70 {"p23", VertexAttribType::kFloat4, SkSLType::kFloat4},
71 {"prevPoint", VertexAttribType::kFloat2, SkSLType::kFloat2},
72 {"stroke", VertexAttribType::kFloat2, SkSLType::kFloat2},
73 {"depth", VertexAttribType::kFloat, SkSLType::kFloat},
74 {"ssboIndices", VertexAttribType::kUInt2, SkSLType::kUInt2}};
75
76 static constexpr Attribute kAttributesWithCurveType[] = {
77 {"p01", VertexAttribType::kFloat4, SkSLType::kFloat4},
78 {"p23", VertexAttribType::kFloat4, SkSLType::kFloat4},
79 {"prevPoint", VertexAttribType::kFloat2, SkSLType::kFloat2},
80 {"stroke", VertexAttribType::kFloat2, SkSLType::kFloat2},
81 {"depth", VertexAttribType::kFloat, SkSLType::kFloat},
82 {"curveType", VertexAttribType::kFloat, SkSLType::kFloat},
83 {"ssboIndices", VertexAttribType::kUInt2, SkSLType::kUInt2}};
84
85 static constexpr SkSpan<const Attribute> kAttributes[2] = {kAttributesWithCurveType,
86 kBaseAttributes};
87
88 } // namespace
89
TessellateStrokesRenderStep(bool infinitySupport)90 TessellateStrokesRenderStep::TessellateStrokesRenderStep(bool infinitySupport)
91 : RenderStep(RenderStepID::kTessellateStrokes,
92 Flags::kRequiresMSAA | Flags::kPerformsShading,
93 /*uniforms=*/{{"affineMatrix", SkSLType::kFloat4},
94 {"translate", SkSLType::kFloat2},
95 {"maxScale", SkSLType::kFloat}},
96 PrimitiveType::kTriangleStrip,
97 kDirectDepthGreaterPass,
98 /*vertexAttrs=*/ {},
99 /*instanceAttrs=*/kAttributes[infinitySupport])
100 , fInfinitySupport(infinitySupport) {}
101
~TessellateStrokesRenderStep()102 TessellateStrokesRenderStep::~TessellateStrokesRenderStep() {}
103
vertexSkSL() const104 std::string TessellateStrokesRenderStep::vertexSkSL() const {
105 // TODO: Assumes vertex ID support for now, max edges must equal
106 // skgpu::tess::FixedCountStrokes::kMaxEdges -> (2^14 - 1) -> 16383
107 return SkSL::String::printf(
108 "float edgeID = float(sk_VertexID >> 1);\n"
109 "if ((sk_VertexID & 1) != 0) {"
110 "edgeID = -edgeID;"
111 "}\n"
112 "float2x2 affine = float2x2(affineMatrix.xy, affineMatrix.zw);\n"
113 "float4 devAndLocalCoords = tessellate_stroked_curve("
114 "edgeID, 16383, affine, translate, maxScale, p01, p23, prevPoint,"
115 "stroke, %s);\n"
116 "float4 devPosition = float4(devAndLocalCoords.xy, depth, 1.0);\n"
117 "stepLocalCoords = devAndLocalCoords.zw;\n",
118 fInfinitySupport ? "curve_type_using_inf_support(p23)" : "curveType");
119 }
120
writeVertices(DrawWriter * dw,const DrawParams & params,skvx::uint2 ssboIndices) const121 void TessellateStrokesRenderStep::writeVertices(DrawWriter* dw,
122 const DrawParams& params,
123 skvx::uint2 ssboIndices) const {
124 SkPath path = params.geometry().shape().asPath(); // TODO: Iterate the Shape directly
125
126 int patchReserveCount = FixedCountStrokes::PreallocCount(path.countVerbs());
127 // Stroke tessellation does not use fixed indices or vertex data, and only needs the vertex ID
128 static const BindBufferInfo kNullBinding = {};
129 // TODO: All HW that Graphite will run on should support instancing ith sk_VertexID, but when
130 // we support Vulkan+Swiftshader, we will need the vertex buffer ID fallback unless Swiftshader
131 // has figured out how to support vertex IDs before then.
132 Writer writer{fInfinitySupport ? kAttribs : kAttribsWithCurveType,
133 *dw,
134 kNullBinding,
135 kNullBinding,
136 patchReserveCount};
137 writer.updatePaintDepthAttrib(params.order().depthAsFloat());
138 writer.updateSsboIndexAttrib(ssboIndices);
139
140 // The vector xform approximates how the control points are transformed by the shader to
141 // more accurately compute how many *parametric* segments are needed.
142 // getMaxScale() returns -1 if it can't compute a scale factor (e.g. perspective), taking the
143 // absolute value automatically converts that to an identity scale factor for our purposes.
144 writer.setShaderTransform(wangs_formula::VectorXform{params.transform().matrix()},
145 params.transform().maxScaleFactor());
146
147 SkASSERT(params.isStroke());
148 writer.updateStrokeParamsAttrib({params.strokeStyle().halfWidth(),
149 params.strokeStyle().joinLimit()});
150
151 // TODO: If PatchWriter can handle adding caps to its deferred patches, and we can convert
152 // hairlines to use round caps instead of square, then StrokeIterator can be deleted entirely.
153 // Besides being simpler, PatchWriter already has what it needs from the shader matrix and
154 // stroke params, so we don't have to re-extract them here.
155 SkMatrix shaderMatrix = params.transform();
156 SkStrokeRec stroke{SkStrokeRec::kHairline_InitStyle};
157 stroke.setStrokeStyle(params.strokeStyle().width());
158 stroke.setStrokeParams(params.strokeStyle().cap(),
159 params.strokeStyle().join(),
160 params.strokeStyle().miterLimit());
161 StrokeIterator strokeIter(path, &stroke, &shaderMatrix);
162 while (strokeIter.next()) {
163 using Verb = StrokeIterator::Verb;
164 const SkPoint* p = strokeIter.pts();
165 int numChops;
166
167 // TODO: The cusp detection logic should be moved into PatchWriter and shared between
168 // this and StrokeTessellator.cpp, but that will require updating a lot of SkGeometry to
169 // operate on float2 (skvx) instead of the legacy SkNx or SkPoint.
170 switch (strokeIter.verb()) {
171 case Verb::kContourFinished:
172 writer.writeDeferredStrokePatch();
173 break;
174 case Verb::kCircle:
175 // Round cap or else an empty stroke that is specified to be drawn as a circle.
176 writer.writeCircle(p[0]);
177 [[fallthrough]];
178 case Verb::kMoveWithinContour:
179 // A regular kMove invalidates the previous control point; the stroke iterator
180 // tells us a new value to use.
181 writer.updateJoinControlPointAttrib(p[0]);
182 break;
183 case Verb::kLine:
184 writer.writeLine(p[0], p[1]);
185 break;
186 case Verb::kQuad:
187 if (ConicHasCusp(p)) {
188 // The cusp is always at the midtandent.
189 SkPoint cusp = SkEvalQuadAt(p, SkFindQuadMidTangent(p));
190 writer.writeCircle(cusp);
191 // A quad can only have a cusp if it's flat with a 180-degree turnaround.
192 writer.writeLine(p[0], cusp);
193 writer.writeLine(cusp, p[2]);
194 } else {
195 writer.writeQuadratic(p);
196 }
197 break;
198 case Verb::kConic:
199 if (ConicHasCusp(p)) {
200 // The cusp is always at the midtandent.
201 SkConic conic(p, strokeIter.w());
202 SkPoint cusp = conic.evalAt(conic.findMidTangent());
203 writer.writeCircle(cusp);
204 // A conic can only have a cusp if it's flat with a 180-degree turnaround.
205 writer.writeLine(p[0], cusp);
206 writer.writeLine(cusp, p[2]);
207 } else {
208 writer.writeConic(p, strokeIter.w());
209 }
210 break;
211 case Verb::kCubic:
212 SkPoint chops[10];
213 float T[2];
214 bool areCusps;
215 numChops = FindCubicConvex180Chops(p, T, &areCusps);
216 if (numChops == 0) {
217 writer.writeCubic(p);
218 } else if (numChops == 1) {
219 SkChopCubicAt(p, chops, T[0]);
220 if (areCusps) {
221 writer.writeCircle(chops[3]);
222 // In a perfect world, these 3 points would be be equal after chopping
223 // on a cusp.
224 chops[2] = chops[4] = chops[3];
225 }
226 writer.writeCubic(chops);
227 writer.writeCubic(chops + 3);
228 } else {
229 SkASSERT(numChops == 2);
230 SkChopCubicAt(p, chops, T[0], T[1]);
231 if (areCusps) {
232 writer.writeCircle(chops[3]);
233 writer.writeCircle(chops[6]);
234 // Two cusps are only possible if it's a flat line with two 180-degree
235 // turnarounds.
236 writer.writeLine(chops[0], chops[3]);
237 writer.writeLine(chops[3], chops[6]);
238 writer.writeLine(chops[6], chops[9]);
239 } else {
240 writer.writeCubic(chops);
241 writer.writeCubic(chops + 3);
242 writer.writeCubic(chops + 6);
243 }
244 }
245 break;
246 }
247 }
248 }
249
writeUniformsAndTextures(const DrawParams & params,PipelineDataGatherer * gatherer) const250 void TessellateStrokesRenderStep::writeUniformsAndTextures(const DrawParams& params,
251 PipelineDataGatherer* gatherer) const {
252 // TODO: Implement perspective
253 SkASSERT(params.transform().type() < Transform::Type::kPerspective);
254
255 SkDEBUGCODE(UniformExpectationsValidator uev(gatherer, this->uniforms());)
256
257 // affineMatrix = float4 (2x2 of transform), translate = float2, maxScale = float
258 // Column-major 2x2 of the transform.
259 SkV4 upper = {params.transform().matrix().rc(0, 0), params.transform().matrix().rc(1, 0),
260 params.transform().matrix().rc(0, 1), params.transform().matrix().rc(1, 1)};
261 gatherer->write(upper);
262
263 gatherer->write(SkPoint{params.transform().matrix().rc(0, 3),
264 params.transform().matrix().rc(1, 3)});
265
266 gatherer->write(params.transform().maxScaleFactor());
267 }
268
269 } // namespace skgpu::graphite
270